Форум НПФ  

Вернуться   Форум НПФ "ТОПОМАТИК" > Программные продукты "Топоматик Robur" на платформе "Robur AP3" > Средства разработки Топоматик Robur > Вопросы и ответы
Ник
Пароль
Регистрация FAQ Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 12-06-2017, 13:49
Gavrishuk_IM Gavrishuk_IM вне форума
Новичок
 
Регистрация: Feb 2012
Адрес: Иркутск
Сообщения: 19
По умолчанию Класс EditTableDlg

Здравствуйте! Пытаюсь использовать Topomatic.Alg.Runtime.Dialogs.EditTableDlg для редактирования своих табличных данных. Дайте пожалуйста пример, по какому принципу и на основе какого класса с интерфейсом IEnumerable лучше формировать wrapper для метода EditTableDlg.Execute
И как в нем описывается заголовок таблицы?
Ответить с цитированием
  #2  
Старый 12-07-2017, 0:47
Василий Гончаров Василий Гончаров вне форума
Разработчик Robur
 
Регистрация: Oct 2016
Сообщения: 26
По умолчанию

Добрый вечер!

Класс Topomatic.Alg.Runtime.Dialogs.EditTableDlg предназначен для использования в том случае, если Вам необходимо отобразить простой диалог с одной таблицей. Если вам необходимо встроить таблицу в Ваше собственное окно, можно воспользоваться фрэймом Topomatic.Alg.Runtime.Dialogs.EditTableFrame или использовать компонет Topomatic.Controls.ObjectInspection.RoburPropertyG rid. В последних сборках программы EditTableDlg и EditTableFrame перенесены в пространстов имен Topomatic.Controls.Dialogs.

Topomatic.Alg.Runtime.Dialogs.EditTableDlg содержит несколько перекрытых методов Execute. Все они возвращают True в случае если пользователь нажал кнопку Ок, и False в остальных случаях. Методы принимают следующие параметры:
caption - строка заголовка диалога
wrapper - данные по которым будет строится таблица
readOnly - отображать таблицу в режиме только для чтения, без возможности редактирования
maximized - развернуть диалог на весь экран при открытии
selected - индекс выбранного элемента
count - количество выбранных элементов

В самом простом случае, для того чтобы отобразить таблицу с данными Вам достаточно того, чтобы объект который Вы передаете в качестве wrapper являлся перечислением объектов, представляющих из себя строки таблицы. Этого достаточно для того чтобы таблица открылась в режиме просмотра, без редактирования.

Для редактирования необходимо дополнительно поддержать интерфейсы IList и Topomatic.ComponentModel.IActivator (не путать с System.ComponentModel.IActivator). Кроме того можно поддержать интерфейс System.ComponentModel.IChangeTracking, если нужно сохранять данные только при нажатии Ок, Topomatic.Controls.ISupportClipboard для работы буфера обмена со строками, Topomaic.ComponentModel.ISupportInterpolation для кнопки интерполяции.

Класс таблицы можно отнаследовать от Topomatic.Alg.Runtime.Wrappers.SimpleChangeTrackin gWrapper, это позолит нам не реализовывать интерфесы IList и IChangeTracking, а воспользоваться уже готовым решением. Но Вы можете реализовать этот интерфесы и вручную.

Каждый объект перечисления - это строчка в таблице. Свойства объекта являются значениями соответствующих столбцов на текущей строчке, а аттрибуты [DisplayName] и [Category] из System.ComponentModel позволяют задать название столбца и объединить несколько столбцов под общим заголовком соответственно. Кроме того полезным для Вас наверняка окажется аттрибут [PropertyProvider(StationPropertyProvider)] из Topomatic.Alg.Runtime.Design позволяющий представить свойство типа double представляющее расстояние от начала трассы в виде двух столбцов ПК и Плюс.

Итак, предположим, что в качестве исходных данных у нас есть трасса и список в виде пар <Расстояние по трассе, значение смещения>, и нам необходимо вывести этот список в диалоге таблицы. Она будет состоять из трех столбцов: ПК ПЛЮС СМЕЩЕНИЕ.
Для этого нам потребуется таблица такого вида:
Код:
sealed class SimpleWrapper : SimpleChangeTrackingWrapper, IActivator, ISupportInterpolation, ISupportClipboard { //Поддержка интерфейса IStationingContainer //она необходима для работы StationPropertyProvider public sealed class Row : IStationingContainer { private IAlgStationing m_Stationing = null; private SimpleWrapper m_Wrapper; private double m_Station; private double m_Offset; internal Row(SimpleWrapper wrapper, IAlgStationing stationing, double station, double offset) { m_Wrapper = wrapper; m_Stationing = stationing; m_Station = station; m_Offset = offset; } //Это свойство возвращает таблицу пикетажа, отображать в таблице его не нужно //Поэтому используем аттрибут [Browsable] из System.ComponentModel [Browsable(false)] public IAlgStationing Stationing { get { return m_Stationing; } } [PropertyProvider(typeof(StationPropertyProvider))] public double Station { get { return m_Station; } set { m_Station = value; //Уведомляем враппер о изменениях m_Wrapper.IsChanged = true; } } [DisplayName("Смещение, м")] public double Offset { get { return m_Offset; } set { m_Offset = value; //Уведомляем враппер о изменениях m_Wrapper.IsChanged = true; } } } //Список пар <Расстояние по трассе, смещение> private List<KeyValuePair<double, double>> m_Pairs = null; //Подобъект по которому мы берем пикетаж private Alignment m_Alignment = null; public SimpleWrapper(Alignment alignment, List<KeyValuePair<double, double>> pairs) { m_Alignment = alignment; m_Pairs = pairs; //Создаем и заполняем внутренний список строк SimpleChangeTrackingWrapper Items = new List<object>(m_Pairs.Count); for (int i = 0; i < m_Pairs.Count; i++) { var pair = m_Pairs[i]; Items.Add(new Row(this, m_Alignment.Stationing, pair.Key, pair.Value)); } } #region IActivator Members //Можно ли создать объект public bool CanCreateInstance { get { return true; } } //Создает объект, в нашем случае пустую строку public object CreateInstance() { return new Row(this, m_Alignment.Stationing, 0.0, 0.0); } #endregion #region ISupportInterpolation Members //Можно ли интерполировать значение public bool CanInterpolate { get { return true; } } //Выполняем интерполяцию между first и seсond и помещаем результат в result public bool Interpolate(object first, object second, object result) { var f = (first as Row); //первая строка var s = (second as Row); //вторая строка var r = (result as Row); //строка результата if ((f != null) && (s != null) && (r != null)) { //Для простоты мы при интерполяции заполняем новую строчку занчениями посередине r.Station = (f.Station + s.Station) * 0.5; r.Offset = (f.Offset + s.Offset) * 0.5; return true; } return false; } #endregion #region ISupportClipboard Members //Идентификатор содержимого, по нему мы определяем, можно ли вставить значение из буфера обмена в нашу таблицу public string AliasName { get { return "OurSimpleWrapperClipboardAliasName"; } } //Можно скопировать public bool CanCopy { get { return true; } } //Можно вставить public bool CanPaste { get { return true; } } //Вставку выполняем как загрузку данных public void Load(object obj, Topomatic.Stg.StgNode node) { //строка, которую заполняем данными var item = obj as Row; if (item != null) { item.Station = node.GetDouble("Station", 0.0); item.Offset = node.GetDouble("Offset", 0.0); } } //Копирование это сохранение данных public void Save(object obj, Topomatic.Stg.StgNode node) { //строка, данные которой сохраняем var item = obj as Row; if (item != null) { node.AddDouble("Station", item.Station); node.AddDouble("Offset", item.Offset); } } #endregion //Пользователь нажал Ок нам необходимо обновить данные в нашем списке пар public override void AcceptChanges() { m_Pairs.Clear(); for (int i = 0; i < Items.Count; i++) { var row = (Row)Items[i]; m_Pairs.Add(new KeyValuePair<double, double>(row.Station, row.Offset)); } } //Флаг только для чтения, по умолчанию false public override bool IsReadOnly { get { return false; } } }


Для того чтобы отобразить нашу таблицу, нам достаточно теперь только вызвать статический метод EditTableDlg.Execute("Наша таблица", new SimpleWrapper(alignment, pairs), false) и если пользователь выбрал Ок, то наш список будет перезаписан.
Ответить с цитированием
  #3  
Старый 12-07-2017, 7:42
Gavrishuk_IM Gavrishuk_IM вне форума
Новичок
 
Регистрация: Feb 2012
Адрес: Иркутск
Сообщения: 19
По умолчанию

Спасибо, все получилось.
Можете еще дать информацию о том, какие атрибуты здесь можно использовать? Очень интересует возможность использования в таблице checkBox и comboBox
И еще - как описать заголовок с тремя уровнями? Например: хочу отобразить в таблице категорию "Левая сторона", которая объединяет подкатегории "Зона 1" и "Зона 2", а в каждой "зоне" будет два столбца - "Ширина" и "Уклон". Через атрибуты DisplayName и Category получилось объединить "ширины" и "уклоны" в "зоны", а вот собрать "зону 1" и "зону 2" в "левую сторону" уже не вышло..(
Ответить с цитированием
  #4  
Старый 12-07-2017, 12:44
Василий Гончаров Василий Гончаров вне форума
Разработчик Robur
 
Регистрация: Oct 2016
Сообщения: 26
По умолчанию

Добрый день!

Описать заголовок с тремя уровнями достаточно просто, достаточно передать в аттрибут [Category] строку, в которой заголовки по уровням разделены символом "|". для Вашего примера у свойства ширина для первой зоны будут аттрибуты [Category("Левая сторона|Зона1")] и [DisplayName("Ширина")], а свойство уклон [Category("Левая сторона|Зона1")] и [DisplayName("Уклон")].

Для того чтобы использовать в качестве свойства список, Вам необходимо описать собственный редактор этого свойства.
Исключение составляют перечисления, в этом случае Вам нужно описать только конвертер типа.

В случае с перечислением, все достаточно просто. Предположим что у Вас есть перечисление из трех зон - ЗОНА1, ЗОНА2 и ЗОНА3 и Вам необходимо вывести их в строке таблицы в выпадающем списке. Вы пишете наследника от Topomatic.ComponentModel.BaseEnumConverter и в конструкторе указываете соответствие между элементами перечисления и строками списка. Потом достаточно над нужным свойством указать PropertyTypeConverter(typeof(<Тип нашего конвертера>))

Код:
//Наше перечисление зон enum Zones { Zone1, Zone2, Zone3 } //Конвертер типа sealed class ZoneTypeEnumConverter : BaseEnumConverter { public ZoneTypeEnumConverter() { base.Dictionary[Zones.Zone1] = "Первая зона"; base.Dictionary[Zones.Zone2] = "Вторая зона"; base.Dictionary[Zones.Zone3] = "Третья зона"; } } ... //Использование нашего свойства class ExampleRow { [DisplayName("Зона"), PropertyTypeConverter(typeof(ZoneTypeEnumConverter ))] public Zones Zone { get; set; } }


Если необходимо описать редактор более сложного свойства, то можно поступить следующим образом. Рассмотрим два наиболее частых случая, когда необходимо либо написать редактор для выпадающего списка с переменным количеством данных, либо редактор свойства с возможностью выбора значения из диалога.

Предположим что у нас есть некий динамический список значение, например список строк. И нам нужно дать пользователю возможность выбрать значение из этого списка. В этом случае для создания редактора можно использовать наследника от Topomatic.ComponentModel.Design.StandardValueEdito r

Код:
//Наш класс строки class Row: List<string> { //Это список всех наших строк [Browsable(false)] public IEnumerable<string> Items { get; } //Строка которую выбирает пользователь [DisplayName("Строка"), PropertyEditor(typeof(OurListValuesEditor))] public string Value { get; set; } } //Наш редактор свойства sealed class OurListValuesEditor : StandardValueEditor { //По факту нам необходимо только переопределить список возвращаемых значений //Здесь нужно вернуть все значения которые может принимать свойство protected override IEnumerable OnGetStandardValues(IPropertyTypeDescriptorContext context) { if (context.Instances.Count > 0) { var row = context.Instances[0] as Row; if (row != null) { foreach(var s in row.Items) { yield return s; } } } } }


Второй пример показывает как нужно поступить, если у Вас есть свойство, редактирование которого возможно из диалога. В этом случае необходимо наследоваться от Topomatic.ComponentModel.PropertyEditor. Например, у нас есть свойство код, которое можно изменять как из какого-то диалога, так и вручную.

Код:
sealed class CodeEditor : PropertyEditor { //Тут мы ставим тип Modal - чтобы таблица нарисовала тсндартную кнопку редактирования справа public override PropertyTypeEditorEditStyle GetEditStyle(IPropertyTypeDescriptorContext context) { return PropertyTypeEditorEditStyle.Modal; } //Здесь мы реализуем редактирование по выбранной кнопке. //Поскольку у нас она только одна, то можно не проверять параметр Button public override object EditValue(IPropertyTypeDescriptorContext context, IPropertyWindowsFormsEditorService editorService, int button) { if (context.Value is int) { var code = (int)context.Value; using (var dlg = new OurDialog()) { dlg.Code = code; if (dlg.ShowDialog() == DailogResult.Ok) return dlg.Code; } } //В случае если ничего не поменялось, возвращаем null return null; } } ... //Использование нашего свойства class ExampleRow { [DisplayName("Код"), PropertyEditor(typeof(CodeEditor))] public int Code { get; set; } }


Возможности использовать CheckBox в таблице на данный момент нет, обычно в этом случае мы используем свойство типа bool, в таблице это отображается как Да или Нет в зависимости от значения. Вы можете реализовать свой собственный PropertyEditor и перекрыв необходимые функции отрисовки его реализовать.
Ответить с цитированием
  #5  
Старый 12-07-2017, 17:17
Gavrishuk_IM Gavrishuk_IM вне форума
Новичок
 
Регистрация: Feb 2012
Адрес: Иркутск
Сообщения: 19
По умолчанию

Ок, спасибо, все получилось :-)
И напоследок - можно задать форматирование ячеек через атрибуты (например округлить число до 2 знаков или выровнять)?

Последний раз редактировалось Gavrishuk_IM, 12-08-2017 в 6:13.
Ответить с цитированием
  #6  
Старый 12-08-2017, 20:14
Василий Гончаров Василий Гончаров вне форума
Разработчик Robur
 
Регистрация: Oct 2016
Сообщения: 26
По умолчанию

Добрый вечер!

Да, это возможно. [DefaultDouble] - округление по умолчанию, [Length] - округление длин, [Radius] - округление радиусов, [Elevation] - округление отметок, [Grade] - округление уклонов, [Area] - округление площадей, [Angle] - отображение значения угла. Точность отображения и кол-во знаков зависят от стандартных настроек программы. Кроме того Вы можете написать собственный PropertyTypeConverter - который будет реализовывать Вашу логику округления.
Ответить с цитированием
Ответ


Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 13:02.

Навигация по основному сайту
Главная Каталог продуктов и услуг Где купить Сопровождение
Вакансии О компании Обзоры Канал на YouTube

vBulletin v3.0.7 , Copyright ©2000-2018, Jelsoft Enterprises Ltd.
Copyright © 2003-2018, НПФ "ТОПОМАТИК".