У меня есть несколько советов для людей, которые говорят, что 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 дизайнер, а затем изменить его обратно. Я предпочитаю этот прием, а не условную компиляцию, потому что с меньшей вероятностью забудет и выпустит неправильную версию .