Здесь показаны различия между двумя версиями данной страницы.
Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
developers:tutorial:createmodel [2019/02/16 17:51] vasya |
developers:tutorial:createmodel [2022/03/15 19:14] (текущий) proxor |
||
---|---|---|---|
Строка 1: | Строка 1: | ||
====== Создание и сохранение модели ====== | ====== Создание и сохранение модели ====== | ||
- | Каждая модель данных в комплексе [[http://www.topomatic.ru|Топоматик Робур]] располагается в рамках отдельного файла, включенного в состав проекта. Для подключения собственной модели к комплексу, необходимо выполнить следующие действия: | + | Каждая модель данных в комплексе [[http://www.topomatic.ru|Топоматик Робур]] располагается в рамках отдельного файла, включенного в состав проекта. С программной точки зрения модель представляет собой экземпляр класса, реализующий интерфейс [[developers:references:topomatic.applicationplatform.core.iprojectmodel|IProjectModel]]. |
+ | Основные методы этого интерфейса это: | ||
+ | * [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|LockWrite()]] - блокирует модель для начала редактирования данных | ||
+ | * [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|UnockWrite()]] - разблокирует модель после окончания редактирования данных | ||
+ | * [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|LockRead()]] - возвращает экземпляр класса, реализующий структуру данных конкретной модели | ||
+ | |||
+ | Для чтения данных из модели используется метод [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|LockRead()]]. Если данные модели необходимо изменить, то перед вызовом [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|LockRead()]] необходимо вызвать [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|LockWrite()]], а после [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|UnockWrite()]]. | ||
+ | <note> | ||
+ | После вызова метода [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|LockWrite()]] файл модели становится недоступным для редактирования другими пользователями до того момента, пока не будет вызван метод [[developers:references:topomatic.applicationplatform.core.iprojectmodel.lockwrite|UnockWrite()]]. | ||
+ | </note> | ||
+ | |||
+ | Для подключения собственной модели к комплексу, необходимо выполнить следующие действия: | ||
+ | * Создать класс модели, реализующий интерфейс [[developers:references:topomatic.foundationclasses.istatecontroller|IStateController]], это необходимо для того чтобы была возможность определять состояние модели и была ли она изменена. | ||
* Создать класс редактора модели, наследник от [[developers:references:topomatic.applicationplatform.core.modeleditor|ModelEditor]], предназначенный для реализации загрузки, сохранения и открытия модели | * Создать класс редактора модели, наследник от [[developers:references:topomatic.applicationplatform.core.modeleditor|ModelEditor]], предназначенный для реализации загрузки, сохранения и открытия модели | ||
* Написать метод, который возвращает экземпляр наследника от [[developers:references:topomatic.applicationplatform.core.modeleditor|ModelEditor]] и декорировать его атрибутом "cmd" | * Написать метод, который возвращает экземпляр наследника от [[developers:references:topomatic.applicationplatform.core.modeleditor|ModelEditor]] и декорировать его атрибутом "cmd" | ||
Строка 7: | Строка 19: | ||
* В файле *.plugin в секции [[developers:references:core.plugin:cores|cores]] описать вид модели в структуре проекта, а в секции [[developers:references:core.plugin:coreitems|coreitems]] включить модель в состав стандартного проекта. | * В файле *.plugin в секции [[developers:references:core.plugin:cores|cores]] описать вид модели в структуре проекта, а в секции [[developers:references:core.plugin:coreitems|coreitems]] включить модель в состав стандартного проекта. | ||
- | Обычно поведение модели может быть реализовано одним из следующих способов: | + | <note> |
- | * При открытии модели открывается программа предназначенная для работы с файлами данного типа (например файлы ведомостей, текстовые файлы и т.п.) | + | Большая часть методов интерфейса [[developers:references:topomatic.foundationclasses.istatecontroller|IStateController]] реализована в классе [[developers:references:topomatic.foundationclasses.statecontrollerobject|StateControllerObject]]. |
- | * При открытии модели открывается отдельное окно для редактирования (например файлы чертежа) | + | Редактор модели на видовом экране окна план и других системных окон нужно наследовать от [[developers:references:topomatic.applicationplatform.core.planmodeleditor|PlanModelEditor]]. |
- | * При открытии модель отображается в окне плана и других системных окнах (например файлы поверхности, подобъектов и т.п.) | + | Редактор модели в отдельном окне нужно наследовать от [[developers:references:topomatic.applicationplatform.core.documentmodeleditor|DocumentModelEditor]] |
- | + | </note> | |
- | Первый пункт - это поведение по умолчанию для любого файла включенного в проект, для которого не зарегестрирован редактор модели. Для реализации второго и третьего пункта предназначены два абстрактных класса, наследника от [[developers:references:topomatic.applicationplatform.core.modeleditor|ModelEditor]] | + | |
- | * [[developers:references:topomatic.applicationplatform.core.documentmodeleditor|DocumentModelEditor]] - предназначен для создания видового экрана модели в отдельном окне | + | |
- | * [[developers:references:topomatic.applicationplatform.core.documentmodeleditor|PlanModelEditor]] - предназначен для моделей, отображающихся непосредственно на видовом экране окна плана | + | |
Для реализации сохранения и загрузки в программном комплексе [[http://www.topomatic.ru|Топоматик Робур]] используется сборка [[developers:references:topomatic.stg|Topomatic.Stg.dll]]. За сохранение отвечает интерфейс [[developers:references:topomatic.stg.istgserializable|IStgSerializable]], который состоит из двух методов - [[developers:references:topomatic.stg.istgserializable.loadfromstg_topomatic.stg.stgnode|LoadFromStg]] для загрузки | Для реализации сохранения и загрузки в программном комплексе [[http://www.topomatic.ru|Топоматик Робур]] используется сборка [[developers:references:topomatic.stg|Topomatic.Stg.dll]]. За сохранение отвечает интерфейс [[developers:references:topomatic.stg.istgserializable|IStgSerializable]], который состоит из двух методов - [[developers:references:topomatic.stg.istgserializable.loadfromstg_topomatic.stg.stgnode|LoadFromStg]] для загрузки | ||
- | и [[developers:references:topomatic.stg.istgserializable.savetostg_topomatic.stg.stgnode|SaveToStg]] для сохранения. | + | и [[developers:references:topomatic.stg.istgserializable.savetostg_topomatic.stg.stgnode|SaveToStg]] для сохранения. Оба метода в качестве параметра принимают экземпляр класса [[developers:references:topomatic.stg.stgnode|StgNode]]. Методы этого класса позволяют сохранить в узел и загрузить из узла все базовые типы данных, а также дополнительные узлы [[developers:references:topomatic.stg.stgnode|StgNode]] и массивы элементов, используя именованные элементы. |
+ | Кроме того у каждого метода есть перекрытое значение, со значением по умолчанию. Значение по умолчанию будет использовано в том случае, если в загружаемом файле нет такого именованного элемента. Например: | ||
+ | <code csharp> | ||
+ | { | ||
+ | .... | ||
+ | private double m_Value1 = 0.0; | ||
+ | |||
+ | private string m_Value2 = 0.0; | ||
+ | |||
+ | void LoadFromStg(StgNode node) | ||
+ | { | ||
+ | //Если в сохраненном файле нет значения с ключом Value1 будет брошено исключение | ||
+ | m_Value1 = node.GetDouble("Value1"); | ||
+ | //Если в сохраненном файле нет значения с ключом Value2, | ||
+ | //то переменной m_Value2 будет присвоено значение "defaultValue" | ||
+ | m_Value2 = node.GetString("Value2", "defaultValue"); | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Использование именованных элементов позволяет легко расширять и дополнять функции загрузки и сохранения по мере расширения модели данных. | ||
+ | |||
+ | <note> | ||
+ | Если в сохраненном файле отсутствует вложенный узел [[developers:references:topomatic.stg.stgnode|StgNode]] или массив, то функции [[developers:references:topomatic.stg.stgnode.getnode_system.string|GetNode]] и [[developers:references:topomatic.stg.stgnode.getarray_system.string_topomatic.stg.stgtype|GetArray]] вернут пустые элементы. | ||
+ | Проверить наличие именованного значения внутри узла позволяет метод [[developers:references:topomatic.stg.stgcollection.isexists_system.string|IsExists]] | ||
+ | </note> | ||
+ | |||
+ | Создайте и настройте новый [[developers:tutorial:module|модуль]] для подключения к программному комплексу [[http://www.topomatic.ru|Топоматик Робур]]. | ||
+ | |||
+ | С помощью диалогового окна Менеджер ссылок добавьте ссылки на следующие библиотеки: | ||
+ | |||
+ | * [[developers:references:topomatic.cad.foundation|Topomatic.Cad.Foundation.dll]] - базовые математические типы и операции | ||
+ | * [[developers:references:topomatic.controls|Topomatic.Controls.dll]] - базовые диалоги и таблицы | ||
+ | |||
+ | Создайте новый класс Model.cs для реализации нашей модели. | ||
+ | <code csharp> | ||
+ | { | ||
+ | //Класс реализующий структуру данных нашей модели | ||
+ | //для поддержки интерфейса IStateController наследуем от StateControllerObject | ||
+ | class Model : StateControllerObject, IStgSerializable | ||
+ | { | ||
+ | private bool m_ReadOnly = false; | ||
+ | |||
+ | //Строковое значение | ||
+ | public string StringValue = "Строка"; | ||
+ | |||
+ | //Булево значение | ||
+ | public bool BooleanValue = false; | ||
+ | |||
+ | //Значение с плавающей точкой | ||
+ | public double DoubleValue = 10.5; | ||
+ | |||
+ | //Целое значение | ||
+ | public int IntValue = 10; | ||
+ | |||
+ | //Список строковых значений | ||
+ | public List<string> ArrayValues = new List<string>(); | ||
+ | |||
+ | //флаг только для чтения | ||
+ | public override bool ReadOnly | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return m_ReadOnly; | ||
+ | } | ||
+ | |||
+ | set | ||
+ | { | ||
+ | m_ReadOnly = value; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Загрузка из узла | ||
+ | public void LoadFromStg(StgNode node) | ||
+ | { | ||
+ | //Все значения загружаем с указанием значения по умолчанию | ||
+ | BooleanValue = node.GetBoolean("BooleanValue", false); | ||
+ | StringValue = node.GetString("StringValue", "Строка"); | ||
+ | DoubleValue = node.GetDouble("DoubleValue", 10.5); | ||
+ | IntValue = node.GetInt32("IntValue", 10); | ||
+ | ArrayValues.Clear(); | ||
+ | //При загрузке массива указывается тип составляющих массив значений | ||
+ | var array = node.GetArray("ArrayValues", StgType.String); | ||
+ | for (int i = 0; i < array.Count; i++) | ||
+ | { | ||
+ | ArrayValues.Add(array.GetString(i)); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Сохранение в узел | ||
+ | public void SaveToStg(StgNode node) | ||
+ | { | ||
+ | //Сохраняем значения в узел | ||
+ | node.AddBoolean("BooleanValue", BooleanValue); | ||
+ | node.AddString("StringValue", StringValue); | ||
+ | node.AddDouble("DoubleValue", DoubleValue); | ||
+ | node.AddInt32("IntValue", IntValue); | ||
+ | //Сохраняем массив с указанием типа значений | ||
+ | var array = node.AddArray("ArrayValues", StgType.String); | ||
+ | for (int i = 0; i < ArrayValues.Count; i++) | ||
+ | { | ||
+ | array.AddString(ArrayValues[i]); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | И класс Editor.cs реализующий редактор модели | ||
+ | |||
+ | <code csharp> | ||
+ | { | ||
+ | //Класс реализующий редактор нашей модели | ||
+ | class Editor : ModelEditor | ||
+ | { | ||
+ | //Реализация загрузки модели по указанному пути, должна вернуть реализацию класса нашей модели | ||
+ | public override object LoadFromFile(string fullpath) | ||
+ | { | ||
+ | //создаем экземпляр класса модели | ||
+ | var model = new Model(); | ||
+ | //если fullpath null - то необходимо просто вернуть экземпляр класса модели, без загрузки данных | ||
+ | if (fullpath != null) | ||
+ | { | ||
+ | //создаем файловый поток | ||
+ | using (var stream = new FileStream(fullpath, FileMode.Open, FileAccess.Read, FileShare.Read)) | ||
+ | { | ||
+ | //создаем документ для работы с Topomatic.Stg | ||
+ | var document = new StgDocument(); | ||
+ | //загружаем документ из потока в бинарном виде | ||
+ | document.LoadFromStreamAsBinary(stream); | ||
+ | //загружаем данные нашей модели из документа | ||
+ | model.LoadFromStg(document.Body); | ||
+ | } | ||
+ | } | ||
+ | //всегда возвращаем экземпляр модели | ||
+ | return model; | ||
+ | } | ||
+ | |||
+ | //Реализация сохранения модели по указанному пути | ||
+ | public override void SaveToFile(object model, string fullpath) | ||
+ | { | ||
+ | //в качестве параметра model приходит наша модель данных | ||
+ | var m = model as Model; | ||
+ | if (m != null) | ||
+ | { | ||
+ | //создаем файловый поток | ||
+ | using (var stream = new FileStream(fullpath, FileMode.Create, FileAccess.Write, FileShare.None)) | ||
+ | { | ||
+ | //создаем документ для работы с Topomatic.Stg | ||
+ | var document = new StgDocument(); | ||
+ | //сохраняем данные нашей модели в документ | ||
+ | m.SaveToStg(document.Body); | ||
+ | //сохраняем документ в потока в бинарном виде | ||
+ | document.SaveToStreamAsBinary(stream); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Реализация открытия модели по команде "open" | ||
+ | public override IEditorResult Open(IProjectModel model) | ||
+ | { | ||
+ | var cursor = Cursor.Current; | ||
+ | Cursor.Current = Cursors.WaitCursor; | ||
+ | try | ||
+ | { | ||
+ | //В нашем поросто возвращаем реализацию интерфеса IEditorResult | ||
+ | return new EditorResult(); | ||
+ | } | ||
+ | finally | ||
+ | { | ||
+ | Cursor.Current = cursor; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Реализация интерфейса IEditorResult | ||
+ | private class EditorResult : IEditorResult | ||
+ | { | ||
+ | private bool m_Opened; | ||
+ | |||
+ | public EditorResult() | ||
+ | { | ||
+ | m_Opened = true; | ||
+ | } | ||
+ | |||
+ | //Необходимо реализовать флаг, показывающий открыта модель или нет | ||
+ | public bool Opened | ||
+ | { | ||
+ | get | ||
+ | { | ||
+ | return m_Opened; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Необходимо реализовать метод закрытия модели | ||
+ | public void Close() | ||
+ | { | ||
+ | //В нашем случае мы просто управляем флагом и все | ||
+ | m_Opened = false; | ||
+ | } | ||
+ | |||
+ | //И метод перезагрузки модели | ||
+ | public void Reload() | ||
+ | { | ||
+ | //Здесь нам ничего не нужно делать | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | В нашем случае редактор модели получился достаточно простым, поскольку наша модель не реагирует на команду открыть и фактически вся работа с ней будет происходить в отдельной команде для редактирования модели. | ||
+ | Дополнительно необходимо реализовать [[developers:tutorial:dlgandpropertygrid|диалог]] для редактирования данных модели. Его реализацию можно увидеть в исходных кодах примера. | ||
+ | |||
+ | В теле программного модуля необходимо объявить функции для создания и редактирования модели, функцию для создания редактора и регистрации модели. | ||
+ | <code csharp> | ||
+ | partial class Module : Topomatic.ApplicationPlatform.Plugins.PluginInitializator | ||
+ | { | ||
+ | //функция создает экземпляр редактора модели и возвращает его | ||
+ | [cmd("create_testmodel_editor")] | ||
+ | public Editor CreateEditor() | ||
+ | { | ||
+ | return new Editor(); | ||
+ | } | ||
+ | |||
+ | //функция создает новую модель и возвращает её | ||
+ | [cmd("create_testmodel")] | ||
+ | private object CreateModel(object[] args) | ||
+ | { | ||
+ | //получаем текущий активный проект | ||
+ | var project = ApplicationHost.Current.ActiveProject as ModelProject; | ||
+ | Debug.Assert(project != null); | ||
+ | if (project != null) | ||
+ | { | ||
+ | //запускаем групповое изменение свойств | ||
+ | project.TransactionManager.BeginUpdate(); | ||
+ | try | ||
+ | { | ||
+ | string folder; | ||
+ | if ((args != null) && (args.Length > 0) && (args[0] != null)) | ||
+ | { | ||
+ | //В качестве аргумента приходит либо идентификатор каталога внутри проекта | ||
+ | folder = args[0].ToString(); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | //либо мы создаем этот идентификатор самостоятельно | ||
+ | folder = PluginCoreOps.FindModelPathId(PluginCoreOps.CreateFolder(new string[] { "Модели", "Тестовые модели" })); | ||
+ | } | ||
+ | //создаем модель с помощью команды "mkitem" | ||
+ | //в неё мы передаем идентификатор каталога и тип нашей модели | ||
+ | return ApplicationHost.Current.Plugins.Execute("mkitem", new object[] { folder, "testmodel" }); | ||
+ | } | ||
+ | finally | ||
+ | { | ||
+ | project.TransactionManager.EndUpdate(); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | throw new OperationCanceledException(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //функция позволяет редактировать содержимое нашей модели | ||
+ | [cmd("edit_testmodel")] | ||
+ | private void OpenModel(string pathid) | ||
+ | { | ||
+ | //получаем IProjectModel используя идентификатор модели | ||
+ | var project_model = PluginCoreOps.FindModel(pathid); | ||
+ | if (project_model != null) | ||
+ | { | ||
+ | //вызываем блокировку модели на редактирование | ||
+ | project_model.LockWrite(); | ||
+ | try | ||
+ | { | ||
+ | //получаем класс нашей модели | ||
+ | var model = project_model.LockRead() as Model; | ||
+ | if (model != null) | ||
+ | { | ||
+ | //вызываем диалог редактирования нашей модели | ||
+ | if (EditModelDlg.Execute(model)) | ||
+ | project_model.Modified = true; | ||
+ | } | ||
+ | } | ||
+ | finally | ||
+ | { | ||
+ | //снимаем блокировку модели на редактирование | ||
+ | project_model.UnlockWrite(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public override void Initialize(PluginFactory factory) | ||
+ | { | ||
+ | base.Initialize(factory); | ||
+ | //Регестрируем нашу модель в проекте | ||
+ | factory.RegisterModelEditor("testmodel", | ||
+ | new ModelEditorInfo("Тестовая модель|*.testmodelx", | ||
+ | ".testmodelx", | ||
+ | "testmodel", | ||
+ | "Тестовая модель", | ||
+ | "create_testmodel_editor")); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | В файле .plugin нужно разместить нашу модель в секции [[developers:references:core.plugin:cores|cores]] и добавить [[developers:tutorial:cmdattribute|команды]] для её редактирования и создания. | ||
+ | <code javascript> | ||
+ | { | ||
+ | "assemblies": { | ||
+ | "tutorial6": { | ||
+ | "assembly": "tutorial6.dll, tutorial6.ModulePluginHost" | ||
+ | } | ||
+ | }, | ||
+ | "actions": { | ||
+ | "id_create_testmodel": { | ||
+ | "cmd": "create_testmodel \"%0\"", | ||
+ | "title": "Тестовая модель" | ||
+ | }, | ||
+ | "id_edit_testmodel": { | ||
+ | "cmd": "edit_testmodel \"%0\"", | ||
+ | "title": "Редактировать..." | ||
+ | } | ||
+ | }, | ||
+ | "contexts": { | ||
+ | "ctx_mkitem": { | ||
+ | "items": [ | ||
+ | "id_create_testmodel \"%0\"" | ||
+ | ] | ||
+ | }, | ||
+ | "testmodel.context": { | ||
+ | "priority": 1001, | ||
+ | "items": [ | ||
+ | { "default": "$(if,$(opened,%0),core.id_close \"%0\" \"Скрыть модель\",core.id_open \"%0\" \"Показать модель\")" }, | ||
+ | "id_edit_testmodel \"%0\"", | ||
+ | "-", | ||
+ | "core.id_rmitem \"%0\"" | ||
+ | ] | ||
+ | } | ||
+ | }, | ||
+ | "cores": { | ||
+ | "testmodel": { | ||
+ | "title": "$(referencename,%0)", | ||
+ | "description": "Тестовая модель %0", | ||
+ | "icon": "ic_file", | ||
+ | "statusicon": "", | ||
+ | "flags": "$(modelflags,%0)", | ||
+ | "menu": "testmodel.context \"%0\"" | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | Обратите внимание на секцию [[developers:references:core.plugin:contexts|contexts]]. Для добавления возможности создать нашу тестовую модель из контекстного меню каталога проекта, мы добавляем функцию создания нашей модели в системное меню "ctx_mkitem". | ||
+ | В результате мы получим возможность добавлять в проект наши тестовые модели и редактировать их содержимое по команде "Редактировать" в контекстном меню модели. | ||
+ | {{ :developers:tutorial:createmodel:contexmenu.png?direct&600 |}} | ||
+ | <note>[[developers:tutorial:tutorialcode|Исходный код]] примера расположен в проекте **"tutorial6"**.</note> |