====== Таблицы и диалоги ======
Для быстрого и удобного оформления диалогов программный комплекс [[http://www.topomatic.ru|Топоматик Робур]] предлагает несколько разных классов, в том числе [[developers:references:topomatic.controls.dialogs.simpledlg|SimpleDlg]] - это стандартный класс, предназначенный для создания модальных диалогов. Он поддерживает стандартный стиль оформления, характерный для всех диалогов программного комплекса, и обладает простым и удобным функционалом.
Для вывода простых информационных сообщений и сообщений о ошибках используется [[developers:references:topomatic.controls.dialogs.messagedlg|MessageDlg]]. Достаточно вызвать статический метод [[developers:references:topomatic.controls.dialogs.messagedlg.show_system.string|MessageDlg.Show]] с требуемым сообщением.
Достаточно создать наследника от [[developers:references:topomatic.controls.dialogs.simpledlg|SimpleDlg]] и перекрыть два метода:
* **DoInit()** - метод вызывается при инициализации диалога, на нём необходимо инициализировать содержимое диалога
* **DoCommit()** - метод вызывается, если пользователь выбрал кнопку ОК на диалоге. Необходимо проверить введённые данные, и в качестве результата вернуть - являются ли данные корректными. В случае если данные некорректны диалог не будет закрыт.
Метод **DoGetModifiedWarning** класса [[developers:references:topomatic.controls.dialogs.simpledlg|SimpleDlg]] может быть использован, если необходимо предупредить пользователя о том, что он нажимает кнопку Отмена, а на диалог были внесены изменения. Для этого метод должен вернуть есть ли на диалоге какие-либо изменения.
Достаточно часто бывает необходимо представить данные в виде редактируемой таблицы. Программный комплекс [[http://www.topomatic.ru|Топоматик Робур]] предоставляет для этого элемент управления [[developers:references:topomatic.controls.dialogs.edittableframe|EditTableFrame]]. Этот элемент предназначен для отображения таблицы со всеми необходимыми кнопками управления.
Для отображения диалогов, состоящих из одной таблицы можно использовать класс [[developers:references:topomatic.controls.dialogs.edittabledlg|EditTableDlg]]. Для этого необходимо вызвать статический метод класса [[developers:references:topomatic.controls.dialogs.edittabledlg.execute_system.string_system.collections.ienumerable_system.boolean_system.boolean|EditTableDlg.Execute]]
Для того чтобы заполнить таблицу данными необходимо присвоить свойство [[developers:references:topomatic.controls.dialogs.edittableframe.wrapper|Wrapper]] у экземпляра [[developers:references:topomatic.controls.dialogs.edittableframe|EditTableFrame]].
Поведение и вид элемента управления элемент управления [[developers:references:topomatic.controls.dialogs.edittableframe|EditTableFrame]] зависят от реализации класса, назначенного свойству [[developers:references:topomatic.controls.dialogs.edittableframe.wrapper|Wrapper]] и поддержки следующих интерфейсов:
* Интерфейс [[developers:references:system.collections.ienumarable|IEnumerable]], таблица доступна только для редактирования элементов, удаление и добавление строк недоступно
* Интерфейсы [[developers:references:system.collections.ilist|IList]] и [[developers:references:topomatic.componentmodel.iactivator|IActivator]], доступно добавление и удаление строк
* Интерфейс [[developers:references:topomatic.controls.isupportclipboard|ISupportClipboard]], поддерживается возможность копирования и вставки строк через буфер обмена
* Интерфейс [[developers:references:topomatic.componentmodel.isupportinterpolation|ISupportInterpolation]], поддерживается возможность интерполирования между строками таблицы
Для более быстрого создания таблиц можно воспользоваться сервисным классом [[developers:references:topomatic.alg.runtime.wrappers.simplechangetrackingwrapper|SimpleChangeTrackingWrapper]]. Этот класс реализует интерфейсы [[developers:references:system.collections.ilist|IList]] и [[developers:references:system.componentmodel.ichangetracking|IChangeTracking]]. При его использовании необходимо на конструкторе создать объекты и заполнить ими свойство **Items**, а на перекрытой функции AcceptChanges заполнить данные модели по объектам этого свойства. Также необходимо поддержать интерфейс [[developers:references:topomatic.componentmodel.iactivator|IActivator]]
Каждый элемент перечисления описывает строку в таблице. Заголовок таблицы строится на основании списка свойств этого элемента, используя атрибуты **[DisplayName]** и **[Category]**. Например, если элемент перечисления реализован следующим образом:
...
class Item
{
[DisplayName("Пикет"), Category("Узлы")]
public string Pk
{
get;set;
}
[DisplayName("Плюс"), Category("Узлы")]
public string Plus
{
get;set;
}
[DisplayName("Отметка, м"), Category("Узлы"), DefaultDouble]
public double Elevation
{
get;set;
}
}
...
Наша таблица будет выглядеть примерно так:
{{ :developers:tutorial:dlgandpropertygrid:simple_table.png?direct&600 |}}
Заголовок таблицы можно сделать многоуровневым, на нижнем уровне всегда отображается значение атрибута **[DisplayName]** а верхние настраиваются с помощью атрибута **[Category]** с разделением уровней символом "**|**". Например **[Category("Уровень3|Уровень1")]**
Отображение свойств в таблице настраивается с помощью разнообразных атрибутов, наследованных от [[developers:references:topomatic.componentmodel.propertytypeconverter|PropertyTypeConverter]], [[developers:references:topomatic.componentmodel.propertyeditor|PropertyEditor]] и [[developers:references:topomatic.componentmodel.propertyprovider|PropertyProvider]]. Более подробно они описаны в других разделах [[developers:tutorial:start|Руководства]].
В частности, для отображения перечисления в виде выпадающего списка, необходимо написать наследника от [[developers:references:topomatic.componentmodel.baseenumconverter|BaseEnumConverter]]. В котором установить соответствие между значениями перечисления и строками используя свойство **Dictionary**. А затем назначить его на нужное свойство используя атрибут [[developers:references:topomatic.componentmodel.propertytypeconverterattribute|[PropertyTypeConverter]]].
Создайте и настройте новый [[developers:tutorial:module|модуль]] для подключения к программному комплексу [[http://www.topomatic.ru|Топоматик Робур]].
С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки:
* [[developers:references:topomatic.cad.foundation|Topomatic.Cad.Foundation.dll]] - базовые математические типы и операции
* [[developers:references:topomatic.cad.view|Topomatic.Cad.View.dll]] - элемент управления для отображения слоёв моделей
* [[developers:references:topomatic.componentmodel|Topomatic.ComponentModel.dll]] - компоненты используемые для инспектирования свойств объектов
* [[developers:references:topomatic.controls|Topomatic.Controls.dll]] - базовые диалоги и таблицы
* [[developers:references:topomatic.alg.runtime|Topomatic.Alg.Runtime.dll]] - возможность использования класса [[developers:references:topomatic.alg.runtime.wrappers.simplechangetrackingwrapper|SimpleChangeTrackingWrapper]]
Не забудьте поставить флаг «Копировать локально» в «False». Кроме того, необходимо добавить нужные пространства имен в секцию using. Visual Studio позволяет легко сделать это, нажав правой клавишей на имени класса и выбрав добавление директивы using.
Создайте новый класс, назовите его TableWrapper.cs и разместите в нём реализацию таблицы.
//Перечисление из нескольких значений
enum DataEnum
{
Value1,
Value2,
Value3
}
//Структура данных, которая будет хранится в нашей модели
struct DataValue
{
public double Length;
public string TextValue;
public DataEnum Value;
}
//Конвертер перечисления DataEnum в строковое представление и обратно
class DataEnumConverter : BaseEnumConverter
{
public DataEnumConverter()
{
Dictionary[DataEnum.Value1] = "Значение1";
Dictionary[DataEnum.Value2] = "Значение2";
Dictionary[DataEnum.Value3] = "Значение3";
}
}
//Класс реализующий обработку содержимого таблицы
//Для простоты реализации, наследуем его от SimpleChangeTrackingWrapper
class TableWrapper : SimpleChangeTrackingWrapper, IActivator
{
//класс реализующий представление строки таблицы
public class TableRow
{
//Указатель на нашу таблицу, необходим, чтобы при изменении свойств выставить св-во IsChanged
private SimpleChangeTrackingWrapper m_Wrapper;
private double m_Length;
private string m_TextValue;
private DataEnum m_Value;
public TableRow(SimpleChangeTrackingWrapper wrapper, DataValue value)
{
m_Wrapper = wrapper;
m_Length = value.Length;
m_TextValue = value.TextValue;
m_Value = value.Value;
}
//Свойство длина
//Обратите внимание, что оно декорировано атрибутом Length - который позволяет выводить содержимое с точностью длин
[DisplayName("Длина, м"), Category("Верхний заголовок|Нижний заголовок"), Length]
public double Length
{
get
{
return m_Length;
}
set
{
m_Length = value;
//Предупреждаем таблицу, о наличии изменений
m_Wrapper.IsChanged = true;
}
}
//Стоковое свойство
[DisplayName("Строка"), Category("Верхний заголовок|Нижний заголовок")]
public string TextValue
{
get
{
return m_TextValue;
}
set
{
m_TextValue = value;
//Предупреждаем таблицу, о наличии изменений
m_Wrapper.IsChanged = true;
}
}
//Свойство перечисление
[DisplayName("Перечисление"), Category("Верхний заголовок|Другой заголовок")]
//Наш конвертер назначен с помощью атрибута PropertyTypeonverterAttribute
[PropertyTypeConverter(typeof(DataEnumConverter))]
public DataEnum Value
{
get
{
return m_Value;
}
set
{
m_Value = value;
//Предупреждаем таблицу, о наличии изменений
m_Wrapper.IsChanged = true;
}
}
}
//Сохраняем ссылку на данные модели
private List m_Data;
//При создании таблицы, заполняем её содержимое данными из модели
public TableWrapper(List data)
{
m_Data = data;
this.Items = new ListОбратите внимание, что свойство **Length** в строке таблицы декорировано атрибутом [[developers:references:topomatic.cad.foundation.design.lengthattribute|Length]]. Большая часть атрибутов реализованных в сборках программного комплекса, реализованы в пространствах имен **Design**
Теперь напишем реализацию диалога. Для этого добавьте в проект новую форму и назовите её TestSimpleDialog.
{{ :developers:tutorial:dlgandpropertygrid:create_form.png?direct&600 |}}
Откройте окно редактора кода, и сделайте её наследником от [[developers:references:topomatic.controls.dialogs.simpledlg|Topomatic.Controls.Dialogs.SimpleDlg]]. Затем добавьте на форму обычную панель и откройте код дизайнера в файле TestSimpleDialog.Designer.cs
{{ :developers:tutorial:dlgandpropertygrid:add_panel.png?direct&600 |}}
Замените класс панели **panel1** на [[developers:references:topomatic.controls.dialogs.edittableframe|EditTableFrame]] вручную и сохраните файл.
Теперь вернитесь в окно дизайнера и разместите [[developers:references:topomatic.controls.dialogs.edittableframe|EditTableFrame]] на диалоге. После чего оформите код диалога следующим образом:
//Наследуем наш диалог от Topomatic.Controls.Dialogs.SimpleDlg
partial class TestSimpleDialog : Topomatic.Controls.Dialogs.SimpleDlg
{
public TestSimpleDialog()
{
InitializeComponent();
}
protected override void DoInit()
{
base.DoInit();
//на инициализации нам не нужно ничего делать
}
protected override bool DoCommit()
{
var wrapper = (TableWrapper)panel1.Wrapper;
//при нажатии ОК проверяем, были ли изменения в таблице
if (wrapper.IsChanged)
//Если были, то заполняем данные модели
wrapper.AcceptChanges();
return base.DoCommit();
}
//Для удобства использования реализуем статический метод, который принимает данные и показывает диалог
public static bool Execute(List values)
{
using (var dlg = new TestSimpleDialog())
{
//из-за особенностей реализации необходимо инициализировать фрэйм до вызова ShowDialog()
dlg.panel1.Wrapper = new TableWrapper(values);
return dlg.ShowDialog() == DialogResult.OK;
}
}
}
В теле программного модуля объявите команду "test_dlg_and_table", и декорируйте её атрибутом "cmd".
partial class Module : Topomatic.ApplicationPlatform.Plugins.PluginInitializator
{
//Используем статический список для хранения наших данных в качестве модели
private static List m_Values = new List();
[cmd("test_dlg_and_table")]
public void TestDlgAndTable()
{
//Просто вызываем метод у нашего диалога
TestSimpleDialog.Execute(m_Values);
}
}
Задача команды "test_dlg_and_table" показать диалог редактирования данных, которые хранятся в статическом списке **m_Values** и дать возможность их редактировать в виде таблицы.
Теперь необходимо сформировать наш файл .plugin. Заполните его следующим образом.
{
"assemblies": {
"tutorial5": {
"assembly": "tutorial5.dll, tutorial5.ModulePluginHost"
}
},
"actions": {
"id_test_dlg_and_table": {
"cmd": "test_dlg_and_table",
"title": "Диалог и таблица"
}
},
"menubars": {
"rbproj": {
"items": [
{
"id": "test_menu",
"title": "Примеры"
}
]
},
"rbproj.test_menu": {
"items": [
"id_test_dlg_and_table"
]
}
}
}
Здесь мы описываем [[developers:references:core.plugin:actions|actions]] для нашей команды, и добавляем пункт в меню.
Результатом запуска проекта будет появления в главном меню пункта «Примеры», с подпунктом "Диалог и таблица", который будет работать в соответствии с описанным выше алгоритмом. После запуска и выполнения мы увидим следующий результат:
{{ :developers:tutorial:dlgandpropertygrid:result.png?direct&600 |}}
Исходный код примера, вы можете скачать используя эту ссылку
{{ :developers:tutorial:dlgandpropertygrid:tutorial5.zip |Архив с кодом примера}}