Я ЗНАЛ, что должен быть способ сделать это (и я нашел способ сделать это чисто). Решение Sheng - это именно то, что я придумал в качестве временного обходного пути, но после того, как друг указал, что Form
класс в конечном итоге унаследован от abstract
класса, мы ДОЛЖНЫ быть в состоянии сделать это. Если они могут это сделать, мы сможем это сделать.
Мы перешли от этого кода к проблеме
Form1 : Form
Проблема
public class Form1 : BaseForm
...
public abstract class BaseForm : Form
Здесь в игру вступил первоначальный вопрос. Как было сказано ранее, друг указал, что System.Windows.Forms.Form
реализует абстрактный базовый класс. Нам удалось найти ...
Доказательство лучшего решения
Исходя из этого, мы знали, что разработчик может показать класс, реализующий базовый абстрактный класс, но он просто не может показать класс дизайнера, который немедленно реализует базовый абстрактный класс. Между ними должно быть не более 5 промежуточных элементов, но мы протестировали 1 уровень абстракции и изначально пришли к этому решению.
Начальное решение
public class Form1 : MiddleClass
...
public class MiddleClass : BaseForm
...
public abstract class BaseForm : Form
...
Это на самом деле работает, и дизайнер прекрасно это делает, проблема решена ... за исключением того, что у вас есть дополнительный уровень наследования в рабочем приложении, которое было необходимо только из-за неадекватности в конструкторе winforms!
Это не стопроцентное решение, но довольно хорошее. В основном вы #if DEBUG
придумываете изысканное решение.
Усовершенствованное решение
Form1.cs
#if DEBUG
public class Form1 : MiddleClass
#else
public class Form1 : BaseForm
#endif
...
MiddleClass.cs
public class MiddleClass : BaseForm
...
BaseForm.cs
public abstract class BaseForm : Form
...
При этом используется только решение, указанное в «начальном решении», если оно находится в режиме отладки. Идея состоит в том, что вы никогда не будете выпускать производственный режим через отладочную сборку и что вы всегда будете проектировать в режиме отладки.
Дизайнер всегда будет работать с кодом, созданным в текущем режиме, поэтому вы не можете использовать конструктор в режиме выпуска. Однако, если вы проектируете в режиме отладки и выпускаете код, построенный в режиме выпуска, все в порядке.
Единственное верное решение - это проверить режим разработки с помощью директивы препроцессора.
@smelch, есть решение получше, без необходимости создавать средний элемент управления даже для отладки.
Что мы хотим
Сначала давайте определим последний класс и базовый абстрактный класс.
Теперь все, что нам нужно, это поставщик описания .
Наконец, мы просто применяем атрибут TypeDescriptionProvider к элементу управления Abastract.
И это все. Среднего контроля не требуется.
И класс провайдера может быть применен к сколь угодно большому количеству абстрактных баз в одном решении.
* РЕДАКТИРОВАТЬ * Также в app.config необходимо следующее:
Спасибо @ user3057544 за предложение.
источник
TypeDescriptionProvider
@Smelch, спасибо за полезный ответ, так как недавно я столкнулся с той же проблемой.
Ниже приведено небольшое изменение в вашем сообщении, чтобы предотвратить предупреждения компиляции (путем помещения базового класса в
#if DEBUG
директиву препроцессора):источник
У меня была аналогичная проблема, но я нашел способ рефакторинга, чтобы использовать интерфейс вместо абстрактного базового класса:
Это может быть применимо не ко всем ситуациям, но по возможности дает более чистое решение, чем условная компиляция.
источник
UserControl
свойство к интерфейсу и указывал на него всякий раз, когда мне нужно было получить к нему прямой доступ. В своих реализациях интерфейса я расширяю UserControl и устанавливаю дляUserControl
свойства значениеthis
Я использую решение в этом ответе на другой вопрос, связанный с этой статьей . В статье рекомендуется использовать индивидуальную
TypeDescriptionProvider
и конкретную реализацию абстрактного класса. Дизайнер спросит у настраиваемого провайдера, какие типы использовать, и ваш код может вернуть конкретный класс, чтобы дизайнер был доволен, пока вы полностью контролируете, как абстрактный класс выглядит как конкретный.Обновление: я включил задокументированный образец кода в свой ответ на этот другой вопрос. Код там работает, но иногда мне приходится проходить цикл очистки / сборки, как указано в моем ответе, чтобы заставить его работать.
источник
У меня есть несколько советов для людей, которые говорят, что работа
TypeDescriptionProvider
Хуана Карлоса Диаса не работает и не любит условную компиляцию:Прежде всего, вам, возможно, придется перезапустить Visual Studio, чтобы изменения в вашем коде работали в конструкторе форм (мне пришлось, простая перестройка не работала - или не каждый раз).
Я представлю свое решение этой проблемы для случая абстрактной базовой формы. Допустим, у вас есть
BaseForm
класс, и вы хотите, чтобы любые формы, основанные на нем, можно было проектировать (так оно и будетForm1
). То,TypeDescriptionProvider
что было представлено Хуаном Карлосом Диасом, мне тоже не подошло. Вот как я заставил это работать, объединив его с решением MiddleClass (по smelch), но без#if DEBUG
условной компиляции и с некоторыми исправлениями:Обратите внимание на атрибут в классе BaseForm. Затем вам просто нужно объявить
TypeDescriptionProvider
и два средних класса , но не волнуйтесь, они невидимы и не имеют отношения к разработчику Form1 . Первый реализует абстрактные члены (и делает базовый класс не абстрактным). Вторая пуста - просто нужна для работы дизайнера форм VS. Затем вы относите второй средний класс кTypeDescriptionProvider
офBaseForm
. Без условной компиляции.У меня были еще две проблемы:
Первая проблема (у вас ее может не быть, потому что это то, что преследует меня в моем проекте еще в нескольких местах и обычно вызывает исключение «Невозможно преобразовать тип X в тип X»). Я решил это в
TypeDescriptionProvider
, сравнивая имена типов (FullName) вместо сравнения типов (см. Ниже).Вторая проблема. Я действительно не знаю, почему элементы управления базовой формы не могут быть разработаны в классе Form1 и их позиции теряются после изменения размера, но я работал над этим (не очень хорошее решение - если вы знаете что-то лучше, напишите). Я просто вручную перемещаю кнопки BaseForm (которые должны находиться в правом нижнем углу) в их правильные положения в методе, вызываемом асинхронно из события Load BaseForm: в
BeginInvoke(new Action(CorrectLayout));
моем базовом классе есть только кнопки «ОК» и «Отмена», поэтому дело простое.А вот и немного измененная версия
TypeDescriptionProvider
:И это все!
Вам не нужно ничего объяснять будущим разработчикам форм на основе вашей BaseForm, и им не нужно делать какие-либо трюки для создания своих форм! Я думаю, что это наиболее чистое решение, какое только может быть (за исключением изменения положения элементов управления).
Еще один совет:
Если по какой - то причине дизайнер по- прежнему отказывается работать для вас, вы всегда можете сделать простой трюк меняя
public class Form1 : BaseForm
кpublic class Form1 : BaseFormMiddle1
(илиBaseFormMiddle2
) в файле кода, редактируя его в VS форме конструктора , а затем изменить его обратно. Я предпочитаю этот трюк условной компиляции, потому что он с меньшей вероятностью забудет и выпустит неправильную версию .источник
У меня есть совет по поводу решения Хуана Карлоса Диаса. Он отлично работает для меня, но с этим были некоторые проблемы. Когда я запускаю VS и захожу в дизайнер, все работает нормально. Но после запуска решения, затем остановитесь и выйдите из него, а затем попробуйте войти в конструктор, исключение появляется снова и снова до перезапуска VS. Но я нашел решение для этого - все, что нужно сделать, это добавить ниже в свой app.config
источник
Поскольку абстрактный класс
public abstract class BaseForm: Form
выдает ошибку и избегает использования конструктора, я пришел с использованием виртуальных членов. По сути, вместо объявления абстрактных методов я объявил виртуальные методы с минимально возможным телом. Вот что я сделал:Поскольку
DataForm
предполагалось, что это абстрактный класс с абстрактным членомdisplayFields
, я «подделываю» это поведение с помощью виртуальных членов, чтобы избежать абстракции. Дизайнер больше не жалуется, у меня все работает нормально.Это обходной путь более читабельный, но поскольку он не абстрактный, я должен убедиться, что все дочерние классы
DataForm
имеют свою реализациюdisplayFields
. Таким образом, будьте осторожны при использовании этой техники.источник
Конструктор Windows Forms создает экземпляр базового класса вашей формы / элемента управления и применяет результат синтаксического анализа
InitializeComponent
. Вот почему вы можете создать форму, созданную мастером проекта, даже не создавая проект. Из-за этого поведения вы также не можете создать элемент управления, производный от абстрактного класса.Вы можете реализовать эти абстрактные методы и генерировать исключение, когда оно не выполняется в конструкторе. Программист, производный от элемента управления, должен предоставить реализацию, которая не вызывает реализацию вашего базового класса. В противном случае программа вылетела бы.
источник
Вы можете просто условно скомпилировать
abstract
ключевое слово, не вставляя отдельный класс:Это работает при условии, что у
BaseForm
него нет абстрактных методов (abstract
поэтому ключевое слово только предотвращает создание экземпляра класса во время выполнения).источник