Динамическая генерация пользовательского интерфейса в C#
Я разрабатываю приложение для библиотеки. Не крупномасштабная библиотека, но очень маленькая библиотека, где моя основная задача-просто хранить информацию о книгах. Но это библиотечное приложение должно быть в состоянии адаптироваться к частной библиотеке любого профессионала. Например, для юриста, помимо основной информации о книге (название, автор, издатель и т. д.), могут быть другие специальные поля, связанные с книгой (номер дела, номер суда и т. д.). Врач может некоторые другие специальные атрибуты для книги. То же самое касается и других профессий.
поэтому я собираюсь использовать базу данных SQL Server CE, и я надеюсь иметь таблицу книг с обычными атрибутами и по требованию изменить таблицу в соответствии с особыми потребностями (добавить больше столбцов).
но мое беспокойство генерирует GUI динамически для поддержки новых атрибутов.
существуют ли какие-либо подходы к решению динамического поколения GUI?
Я не прошу полный код (который, очевидно, я не получу), но если у вас есть какое-либо кодирование для поддержки подхода, пожалуйста, будьте любезны опубликовать его:)
есть ли что-нибудь, что я должен знать о плюсах, минусах, тупиках, предостережениях или предупреждениях и т. д.?
4 ответов
на стороне модели данных, которую @devnull подобрал, вы описываете реализацию пользовательских полей, а @devnull описывает ЕАВ модель.
есть хорошая статья stackoverflow, которая охватывает шаблоны проектирования для пользовательских полей в приложении:
какие шаблоны проектирования для поддержки пользовательских полей в приложении?
выбор модели данных и поколение пользовательского интерфейса тесно связаны, поэтому вы не можете ответить на Вопрос генерации пользовательского интерфейса, пока вы не решите модель данных / шаблон настраиваемого поля. Моя первоначальная реакция была такой же, как у @devnull на альтернативном подходе, но на самом деле нет отличного решения.
вы можете уменьшить большую сложность, если у вас есть надмножество всех возможных полей и разрешить пользователю включать/отключать те, которые подходят для их домена приложения. Я сделал несколько реализаций пользовательских полей в приложении с очень умными людьми, и это всегда трудный. Если вы достаточно хорошо понимаете домен приложения, вы можете держаться подальше от гибких архитектур uber и сэкономить много горя.
обратите внимание, что важным соображением является необходимость запроса пользовательских полей. Это намного проще, если вам не нужно поддерживать общие запросы. Вы просто слот userdate1, имя пользователя и т. д. и предоставьте таблицу метаданных для меток.
Я не знаю, является ли динамическое изменение таблицы хорошим дизайнерским решением. Вместо этого можно создать таблицу подстановки, в которой можно определить типы сведений, и таблицу сведений о книгах, в которой будут храниться эти сведения. Затем вы можете отобразить эти сведения в разделе редактирования книги в виде datagrid, который имеет типы сведений в виде строк, каждая строка имеет столбец, в котором вы будете редактировать значение. Конечно, детализация книги может быть чем угодно, кроме простого строкового значения, но это смогите быть обращано легко. Надеюсь, я был достаточно ясен :)
------------- ----------------- ----------------
| Books | | BooksDetail | | DetailTypes |
------------- ----------------- ----------------
| ID (PK) | 1 n | ID (PK) | 1 1 | ID (PK) |
| AuthorID | --------> | BookID | -------> | Name |
| Title | | DetailID | | Description |
| Year | | Value | ----------------
------------- -----------------
доступно множество инструментов генерации кода. Некоторые из них генерируют код с легко используемым GUI.
в качестве альтернативы следующие коды могут сделать вашу жизнь проще.
вы можете иметь общую базовую форму для таких объектов:
public partial class BaseForm : Form
{
///////////Event Mechanism///////////
protected internal event ItemStateChanged ItemStateChangeEvent;
protected internal void OnItemStateChanged()
{
if (ItemStateChangeEvent != null)
{
ItemStateChangeEvent();
}
}
///////////Event Mechanism///////////
protected internal Label ErrorMessageTextBox
{
get { return this.errorMessageTextBox; }
set { this.errorMessageTextBox = value; }
}
protected internal ToolStripStatusLabel TotalToolStripStatusLabel
{
get { return this.totalToolStripStatusLabel; }
set { this.totalToolStripStatusLabel = value; }
}
protected internal FormViewMode FormViewMode { get; set; }
public BaseForm()
{
InitializeComponent();
}
}
и общая базовая форма для коллекций:
public partial class CollectionBaseForm : BaseForm
{
protected internal ToolStripMenuItem ReportMenu { get { return this.reportsToolStripMenuItem; } set { this.reportsToolStripMenuItem = value; } }
protected internal DataGridView DataGridView { get {return this.dataGridView1 ;} set {dataGridView1 = value ;} }
protected internal Button SearchButton { get { return btnSearch; } set { btnSearch = value; } }
protected internal Button AddNewButton { get { return btnAddNew; } set { btnAddNew = value; } }
protected internal Button EditButton { get { return btnEdit; } set { btnEdit = value; } }
protected internal Button DeleteButton { get { return btnDelete; } set { btnDelete = value; } }
protected internal Button PickButton { get { return btnPick; } set { btnPick = value; } }
private FormViewMode _formViewMode;
public FormViewMode FormViewMode
{
get
{
return _formViewMode;
}
set
{
_formViewMode = value;
EnableDisableAppropriateButtons(_formViewMode);
}
}
private void EnableDisableAppropriateButtons(FormViewMode FormViewMode)
{
if (FormViewMode == FormViewMode.Collection)
{
AddNewButton.Enabled = true;
EditButton.Enabled = true;
DeleteButton.Enabled = true;
PickButton.Enabled = false;
}
else if (FormViewMode == FormViewMode.Picker)
{
AddNewButton.Enabled = false;
EditButton.Enabled = false;
DeleteButton.Enabled = false;
PickButton.Enabled = true;
}
}
public CollectionBaseForm()
{
InitializeComponent();
this.MaximumSize = this.MinimumSize = this.Size;
this.FormViewMode = FormViewMode.Collection;
}
private void closeToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
}
}
тогда все ваши формы будут иметь то же самое генерал смотрит:
Это может помочь, его не динамичным, но он может быть адаптирован достаточно легко..
http://zxtrix.wordpress.com/2010/11/13/automatic-winforms-ui-generation-from-object-metadata/