У меня есть несколько советов для людей, которые говорят, что работа TypeDescriptionProvider
Хуана Карлоса Диаса не работает и не любит условную компиляцию:
Прежде всего, вам, возможно, придется перезапустить Visual Studio, чтобы изменения в вашем коде работали в конструкторе форм (мне пришлось, простая перестройка не работала - или не каждый раз).
Я представлю свое решение этой проблемы для случая абстрактной базовой формы. Допустим, у вас есть BaseForm
класс, и вы хотите, чтобы любые формы, основанные на нем, можно было проектировать (так оно и будет Form1
). То, TypeDescriptionProvider
что было представлено Хуаном Карлосом Диасом, мне тоже не подошло. Вот как я заставил это работать, объединив его с решением MiddleClass (по smelch), но без#if DEBUG
условной компиляции и с некоторыми исправлениями:
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<BaseForm, BaseFormMiddle2>))] // BaseFormMiddle2 explained below
public abstract class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
}
public abstract void SomeAbstractMethod();
}
public class Form1 : BaseForm // Form1 is the form to be designed. As you see it's clean and you do NOTHING special here (only the the normal abstract method(s) implementation!). The developer of such form(s) doesn't have to know anything about the abstract base form problem. He just writes his form as usual.
{
public Form1()
{
InitializeComponent();
}
public override void SomeAbstractMethod()
{
// implementation of BaseForm's abstract method
}
}
Обратите внимание на атрибут в классе BaseForm. Затем вам просто нужно объявить TypeDescriptionProvider
и два средних класса , но не волнуйтесь, они невидимы и не имеют отношения к разработчику Form1 . Первый реализует абстрактные члены (и делает базовый класс не абстрактным). Вторая пуста - просто нужна для работы дизайнера форм VS. Затем вы относите второй средний класс к TypeDescriptionProvider
оф BaseForm
. Без условной компиляции.
У меня были еще две проблемы:
- Проблема 1. После изменения формы Form1 в дизайнере (или в каком-либо коде) она снова выдавала ошибку (при повторной попытке открыть ее в дизайнере).
- Проблема 2: элементы управления BaseForm были размещены неправильно, когда размер формы Form1 был изменен в дизайнере, а форма была закрыта и снова открыта в дизайнере форм.
Первая проблема (у вас ее может не быть, потому что это то, что преследует меня в моем проекте еще в нескольких местах и обычно вызывает исключение «Невозможно преобразовать тип X в тип X»). Я решил это в TypeDescriptionProvider
, сравнивая имена типов (FullName) вместо сравнения типов (см. Ниже).
Вторая проблема. Я действительно не знаю, почему элементы управления базовой формы не могут быть разработаны в классе Form1 и их позиции теряются после изменения размера, но я работал над этим (не очень хорошее решение - если вы знаете что-то лучше, напишите). Я просто вручную перемещаю кнопки BaseForm (которые должны находиться в правом нижнем углу) в их правильные положения в методе, вызываемом асинхронно из события Load BaseForm: в BeginInvoke(new Action(CorrectLayout));
моем базовом классе есть только кнопки «ОК» и «Отмена», поэтому дело простое.
class BaseFormMiddle1 : BaseForm
{
protected BaseFormMiddle1()
{
}
public override void SomeAbstractMethod()
{
throw new NotImplementedException(); // this method will never be called in design mode anyway
}
}
class BaseFormMiddle2 : BaseFormMiddle1 // empty class, just to make the VS designer working
{
}
А вот и немного измененная версия TypeDescriptionProvider
:
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
return typeof(TBase);
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
И это все!
Вам не нужно ничего объяснять будущим разработчикам форм на основе вашей BaseForm, и им не нужно делать какие-либо трюки для создания своих форм! Я думаю, что это наиболее чистое решение, какое только может быть (за исключением изменения положения элементов управления).
Еще один совет:
Если по какой - то причине дизайнер по- прежнему отказывается работать для вас, вы всегда можете сделать простой трюк меняя public class Form1 : BaseForm
к public class Form1 : BaseFormMiddle1
(или BaseFormMiddle2
) в файле кода, редактируя его в VS форме конструктора , а затем изменить его обратно. Я предпочитаю этот трюк условной компиляции, потому что он с меньшей вероятностью забудет и выпустит неправильную версию .