Я разрабатываю приложение для библиотеки. Не большая библиотека, но очень маленькая библиотека, где моя основная задача - просто хранить информацию о книгах. Но это библиотечное приложение должно быть в состоянии адаптироваться к личной библиотеке любого профессионала. Например, для юриста, помимо основной информации о книге (название, автор, издатель и т. Д.), Могут быть другие специальные поля, связанные с книгой (номер дела, номер суда и т. Д.). Врач может иметь некоторые другие специальные атрибуты для книги. То же самое касается других профессий.
Итак, я собираюсь использовать базу данных SQL Server CE и надеюсь, что у меня будет таблица BOOK с обычными атрибутами и по требованию ALTER таблицы для удовлетворения особых потребностей (добавьте больше столбцов).
Но мое беспокойство генерирует графический интерфейс динамически для поддержки новых атрибутов.
Существуют ли какие-либо подходы к созданию динамического графического интерфейса?
Я не прошу полный код (который, очевидно, не получу), но если у вас есть какой-либо код для поддержки этого подхода, пожалуйста, будьте любезны опубликовать это :)
Что я должен знать о плюсах, минусах, тупиках, предостережениях или предупреждениях и т. д.?
Что касается модели данных, которую подхватил @devnull, вы описываете реализацию настраиваемых полей, а @devnull описывает модель EAV.
Есть хорошая статья stackoverflow, в которой рассматриваются шаблоны проектирования для настраиваемых полей в приложении:
Какие шаблоны проектирования поддерживают настраиваемые поля в приложении?
Выбор модели данных и создание пользовательского интерфейса тесно связаны, поэтому вы не сможете ответить на вопрос о создании пользовательского интерфейса, пока не определитесь со своей моделью данных/шаблоном пользовательского поля. Моя первоначальная реакция была такой же, как у @devnull в отношении альтернативного подхода, но на самом деле отличного решения нет.
Вы можете значительно снизить сложность, если у вас будет расширенный набор всех возможных полей и вы позволите пользователю включать/отключать те из них, которые подходят для его предметной области. Я сделал несколько реализаций настраиваемых полей в приложении с очень умными людьми, и это всегда сложно. Если вы достаточно хорошо разбираетесь в предметной области, вы можете держаться подальше от сверхгибких архитектур и избавить себя от многих проблем.
Обратите внимание, что важным соображением является вопрос о том, нужно ли им запрашивать настраиваемые поля. Это намного проще, если вам не нужно поддерживать общие запросы. Вы просто добавляете userdate1, usern и т. д. и предоставляете таблицу метаданных для меток.
Я не знаю, является ли динамическое изменение таблицы хорошим дизайнерским решением. Вместо этого у вас может быть таблица поиска, в которой вы можете определить типы сведений, и таблица сведений о книгах, в которой вы будете хранить эти сведения. Затем вы можете отобразить эти сведения в разделе редактирования книги в виде сетки данных, в которой типы сведений представлены в виде строк, причем в каждой строке есть столбец, в котором вы будете редактировать значение. Конечно, сведения о книге могут быть чем угодно, кроме простого строкового значения, но с этим можно легко справиться. Надеюсь, я выразился достаточно ясно :)
------------- ----------------- ----------------
| Books | | BooksDetail | | DetailTypes |
------------- ----------------- ----------------
| ID (PK) | 1 n | ID (PK) | 1 1 | ID (PK) |
| AuthorID | --------> | BookID | -------> | Name |
| Title | | DetailID | | Description |
| Year | | Value | ----------------
------------- -----------------
Существует множество инструментов для генерации кода. Некоторые из них генерируют код с удобным графическим интерфейсом.
В качестве альтернативы следующие коды может сделать вашу жизнь более легкой.
У вас может быть общая базовая форма для таких объектов:
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);
}
}
Тогда все ваши формы будут иметь одинаковый общий вид: