При создании пользовательских интерфейсов в WinForms приложениях разработчикам приходится делать неинтересную повторяющуюся работу. Страшно представить сколько человеко-часов во всем мире потрачено на фрагменты пользовательского интерфейса, показанные ниже. Сэкономленное время можно было бы провести с близкими людьми, например, на море…
Форма для редактирования адреса.
Login форма.
В этой статье мы рассмотрим существующие подходы к решению проблемы повторного использования элементов пользовательского интерфейса, предложим еще один и обсудим когда какой способ лучше. Статья предназначена для WinForms разработчиков, знакомых с нашей линейкой контролов.
Самое простое и универсальное, но не визуальное решение для повторного использования элементов пользовательского интерфейса — генерация их из кода. Недостаток этого подхода очевиден — невозможно визуально отредактировать результат. Этот недостаток может компенсировать использование правильных контролов. Например, наш XAF а так же Dashboard по умолчанию генерирует простой UI из метаданных с помощью XtraLayoutControl, который впоследствии пользователь может в рантайме изменить. Пользовательские изменения сохраняются в XML файл и каждый раз после генерации UI происходит загрузка из этого файла. Причем рантайм кастомизация и загрузка/сохранение лайаута в XML это встроенный функционал XtraLayoutControl.
Первым известным автору визуальным решением для создания реюзабельных элементов интерфейса была фича form Visual Inheritance в Delphi. Несколько позже эта фича появилась в Visual Studio, но реализована она до сих пор плохо и доставляет немало головной боли разработчикам компонентов, которые хоть немного сложнее кнопки. Не используйте Visual Inheritance. Вместо этого хорошо работает разбиение пользовательского интерфейса на отдельные UserControl. Например, форму оформления заказа можно разбить на HeaderUserControl, AddressUserControl, OkCancelButtonsUserControl, которые можно использовать сколько угодно на формах в проекте. Это хороший способ, но и он не лишен недостатков. UserControl появляется в Toolbox студии только в том проекте, в котором он создан. При модификации UserControl изменения будут применены везде, где его использовали, а это не всегда хорошо. Так же такое разбиение добавляет сложностей с лайаутом приложения, но если использовать XtraLayoutControl, а при разбиении большой формы создавать не UserControl а DX версию XtraUserControl, то проблем с лайаутом не будет. В 14.1 версии мы серьезно поработали над улучшением этого сценария использования: XtraUserControl умеет транслировать MinimumSize и MaximumSize своих чайлдов на верхний уровень, и ситуация когда XtraUserControl’y достается на форме меньше места чем нужно не должна возникать больше.
Ну и, наконец, расскажем о предлагаемом подходе к реализации реюзабельного UI. Мы написали свой механизм для работы с типовыми шаблонами на XtraLayoutControl.
Если открыть XtraLayoutControl CustomizationForm в дизайнере студии и выставить флажок Show Templates, в списке Hidden Items появятся предопределенные шаблоны. Эта экспериментальная фича станет доступной в версии 14.1.5, которую мы выпускаем сегодня. Пока мы отключили возможность создавать собственные шаблоны, потому что у нас не готов дизайн некоторых элементов, мы надеемся что этот функционал будет доступен к следующему обновлению.
Шаблон содержит информацию о типах контролов, о сборках которые необходимо добавить в проект при бросании контрола из шаблона на DesignSurface, фрагмент InitializeComponent относящийся к выбранным контролам. Пример такого фрагмента показан ниже
public class textEdit1 : DevExpress.XtraEditors.TextEdit {
public void InitializeInstance(DevExpress.XtraEditors.TextEdit target) {
((System.ComponentModel.ISupportInitialize)(target.Properties)).BeginInit();
target.Location = new System.Drawing.Point(0, 0);
target.Name = "textEdit1";
target.Size = new System.Drawing.Size(100, 20);
target.TabIndex = 4;
((System.ComponentModel.ISupportInitialize)(target.Properties)).EndInit();
}
}
При бросании шаблона, получаем следующую последовательность:
-добавить зависимые сборки в проект
-создать инстанс контрола по типу, указанному в шаблоне.
-скомпилировать временную сборку, в которой содержится инициализирующий метод.
— вызвать инициализацию контрола методом из временой сборки.
— добавить инициализированный контрол на DesignSurface.
Самая большая проблема в описанной процедуре — это связи между компонентами. Например, пользователь сделал кастомизацию удобных ему дефолтных значений для XtraGridControl. Если у грида задан DataSource, то в инициализирующем методе будет строчка grid.DataSource = bindingSource1. В шаблоне не будет bindingSource1 и эта строчка выдаст ошибку при инициализации. К сожалению, мы пока не смогли придумать эвристику которая правильно вычищала бы внешние связи в шаблоне. Скорее всего будем выдавать ошибку в этом случае на этапе создания шаблона.
Итак, начиная с 14.1.5 в арсенале наших пользователей появляется еще один визуальный способ повторного использования фрагментов пользовательского интерфейса — шаблоны. В каких ситуациях следует отдавать ему предпочтение?
— Шаблоны, доступны из любого проекта, а не только в проекте где их создали, главное чтобы .NET смог найти все необходимые шаблону сборки в момент инициализации шаблона.
— Не всегда удобно создавать отдельный UserControl в проекте ради нескольких контролов.
— Применение изменений UserControl во всех местах, где он используется не всегда желательно, шаблоны создают независимые копии контролов которые легко модифицировать прямо на форме. Наверное, здесь уместно провести аналогию шаблонов со сниппетами. Языки .NET предоставляют много способов избежать дублирующегося кода, но несмотря на это существуют сниппеты в студии, которые иногда просто удобнее.
Автор: xtraroman