У мене є кілька порад для людей, які говорять, що 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 (які повинні знаходитись у правому нижньому куті) до їх правильних позицій методом, який асинхронно викликається від події навантаження 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 формі конструктора , а потім змінити його назад. Я вважаю за краще цей трюк над умовною компіляцією, оскільки менше шансів забути та випустити неправильну версію .