Здесь показаны различия между двумя версиями данной страницы.
Предыдущая версия справа и слева Предыдущая версия Следующая версия | Предыдущая версия | ||
developers:tutorial:createmodel [2019/02/16 20:25] vasya |
developers:tutorial:createmodel [2022/03/15 19:14] (текущий) proxor |
||
---|---|---|---|
Строка 13: | Строка 13: | ||
Для подключения собственной модели к комплексу, необходимо выполнить следующие действия: | Для подключения собственной модели к комплексу, необходимо выполнить следующие действия: | ||
+ | * Создать класс модели, реализующий интерфейс [[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" | ||
Строка 18: | Строка 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> | ||
Для реализации сохранения и загрузки в программном комплексе [[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]] для загрузки | ||
Строка 53: | Строка 59: | ||
* [[developers:references:topomatic.cad.foundation|Topomatic.Cad.Foundation.dll]] - базовые математические типы и операции | * [[developers:references:topomatic.cad.foundation|Topomatic.Cad.Foundation.dll]] - базовые математические типы и операции | ||
- | * [[developers:references:topomatic.cad.view|Topomatic.Cad.View.dll]] - элемент управления для отображения слоёв моделей | ||
* [[developers:references:topomatic.controls|Topomatic.Controls.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> |