Инструменты пользователя

Инструменты сайта


developers:tutorial:undoredo

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
developers:tutorial:undoredo [2019/03/23 20:27]
vasya
developers:tutorial:undoredo [2022/03/15 19:16] (текущий)
proxor
Строка 14: Строка 14:
  
 Содержащие поля данных,​ списки и словари объекты должны поддерживать интерфейс [[developers:​references:​topomatic.foundationclasses.undo.itransactable|ITransactable]].  ​ Содержащие поля данных,​ списки и словари объекты должны поддерживать интерфейс [[developers:​references:​topomatic.foundationclasses.undo.itransactable|ITransactable]].  ​
-Для реализации поддержки интерфейса [[developers:​references:​topomatic.foundationclasses.undo.itransactable|ITransactable]]объекты наследуются от базового класса [[developers:​references:​topomatic.foundationclasses.updatableobject|UpdatableObject]] и поддерживают интерфейс [[developers:​references:​topomatic.foundationclasses.iitem|IItem]]. + 
 +Для ​совершения изменений модели необходимо выполнить следующую последовательность действий: 
 + 
 +  * Вызвать у родительского объекта,​ в котором происходят изменения,​ метод [[developers:​references:​topomatic.foundationclasses.iupdatable.beginupdate|BeginUpdate]] ​перед началом изменений. 
 +  * Произвести необходимые изменения используя [[developers:​references:​topomatic.foundationclasses.undo.basetransactablefield_1.value|Value]] у [[developers:​references:​topomatic.foundationclasses.undo.basetransactablefield_1|BaseTransactableField<​T>​]] или изменив содержимое ​контейнеров [[developers:​references:​topomatic.foundationclasses.undo.basetransactablelist_1|BaseTransactableList<​T>​]] ​и [[developers:​references:​topomatic.foundationclasses.undo.transactabledictionary_2|TransactableDictionary<​T,​ U>]] 
 +  * Вызвать у родительского объекта,​ в котором происходят изменения,​ метод [[developers:​references:​topomatic.foundationclasses.iupdatable.endupdate|EndUpdate]] после завершения изменений. 
 + 
 +<​note>​При изменении только одного поля [[developers:​references:​topomatic.foundationclasses.undo.basetransactablefield_1|BaseTransactableField<​T>​]] или одного элемента контейнеров [[developers:​references:​topomatic.foundationclasses.undo.basetransactablelist_1|BaseTransactableList<​T>​]] и [[developers:​references:​topomatic.foundationclasses.undo.transactabledictionary_2|TransactableDictionary<​T,​ U>]] допустимо не вызывать методы [[developers:​references:​topomatic.foundationclasses.iupdatable.beginupdate|BeginUpdate]] и [[developers:​references:​topomatic.foundationclasses.iupdatable.endupdate|EndUpdate]]. В этом случае они будут вызваны автоматически перед началом и в конце изменения соответственно.</​note>​ 
 + 
 +Для работы истории изменений,​ необходимо чтобы свойству [[developers:​references:​topomatic.foundationclasses.undo.itransactable.transactionmanager|TransactionManager]] ​интерфейса [[developers:​references:​topomatic.foundationclasses.undo.itransactable|ITransactable]] ​был назначен корректный экземпляр интерфейса [[developers:​references:​topomatic.foundationclasses.undo.itransactionmanager|ITransactionManager]]. 
 +Для этого все основные ​объекты ​программного комплекса [[http://​topomatic.ru|Топоматик Робур]] ​наследуются от базового класса [[developers:​references:​topomatic.foundationclasses.updatableobject|UpdatableObject]] и поддерживают интерфейс [[developers:​references:​topomatic.foundationclasses.iowned|IOwned]].  
 +<​note>​ 
 +Если необходимо отслеживать изменение объекта,​ при изменении каких либо дочерних элементов,​ то необходимо наследоваться от [[developers:​references:​topomatic.foundationclasses.undoobject|UndoObject]]. Это наследник от [[developers:​references:​topomatic.foundationclasses.updatableobject|UpdatableObject]] в котором дополнительно поддерживаются события [[developers:​references:​topomatic.foundationclasses.undoobject.changed|Changed]] и [[developers:​references:​topomatic.foundationclasses.undoobject.undo|Undo]] 
 +</​note>​ 
 + 
 +Класс [[developers:​references:​topomatic.foundationclasses.updatableobject|UpdatableObject]] отвечает за реализацию интерфейса [[developers:​references:​topomatic.foundationclasses.undo.itransactable|ITransactable]],​ а интерфейс [[developers:​references:​topomatic.foundationclasses.iowned|IOwned]] позволяет реализовать свойство [[developers:​references:​topomatic.foundationclasses.undo.itransactable.transactionmanager|TransactionManager]] запрашивая его по цепочке родителей с самого верхнего. В этом случае экземпляр [[developers:​references:​topomatic.foundationclasses.undo.itransactable.transactionmanager|TransactionManager]] назначается самому верхнему элементу в цепочке,​ обычно это наследник от [[developers:​references:​topomatic.foundationclasses.statecontrollerobject|StateControllerObject]],​ а все дочерние элементы получают его автоматически. 
 +<​note>​Часто возникает ситуация,​ когда в модель необходимо внести какие либо изменения,​ а потом либо отменить внесенные изменения без записи в истории либо применить их. В таких случаях перед началом изменений нужно вызвать метод **BeginTransaction** а в конце в либо метод **Commit** чтобы применить изменения,​ либо метод **Rollback** для сброса изменений</​note>​ 
 + 
 +Создайте новую [[developers:​tutorial:​gripsandsnaps|модель и видовой слой]] и подключите к программному комплексу [[http://​www.topomatic.ru|Топоматик Робур]]. 
 + 
 +Измените класс Points.cs. Сделаем его наследником от [[developers:​references:​topomatic.foundationclasses.updatableobject|UpdatableObject]] и поддержим интерфейс [[developers:​references:​topomatic.foundationclasses.iowned|IOwned]],​ а список сохраняющий значения точек заменяем на [[developers:​references:​topomatic.foundationclasses.undo.basetransactablelist_1|BaseTransactableList<​T>​]]. 
 +<code csharp>​ 
 +    //​Класс для хранения точек в нашей модели 
 +    //​поддерживаем интерфейсы IStgSerializable для сохранения и IOwned для определения владельца списка точек 
 +    //​Также поддерживаем интерфейс IObjectDisjoiner для привязок 
 +    class Points : UpdatableObject,​ IStgSerializable,​ IOwned, IObjectDisjoiner 
 +    { 
 +        //​Поддержка цепочки родителей 
 +        private Model m_Owner; 
 + 
 +        //​Список структур с поддержкой истории изменений 
 +        private TransactableList<​Vector2D>​ m_Points; 
 + 
 +        public Points(Model owner) 
 +        { 
 +            m_Owner = owner; 
 +            m_Points = new TransactableList<​Vector2D>​(this);​ 
 +        } 
 + 
 +        //​Метод для добавления точки в список 
 +        public void AddPoint(Vector2D point) 
 +        { 
 +            m_Points.Add(point);​ 
 +        } 
 + 
 +        //​Метод для удаления точки из списка 
 +        public void RemovePoint(int index) 
 +        { 
 +            m_Points.RemoveAt(index);​ 
 +        } 
 + 
 +        //​Метод для получения точки из списка 
 +        public Vector2D this[int index] 
 +        { 
 +            get 
 +            { 
 +                return m_Points[index];​ 
 +            } 
 +            set 
 +            { 
 +                m_Points[index] = value; 
 +            } 
 +        } 
 + 
 +        //​Количество точек в списке 
 +        //​Свойство декорировано атрибутом DisplayName для отображения в инспекторе объектов 
 +        [DisplayName("​Количество точек"​)] 
 +        public int Count 
 +        { 
 +            get 
 +            { 
 +                return m_Points.Count;​ 
 +            } 
 +        } 
 + 
 +        //​Владелец нашего списка точек, у нас это наша модель 
 +        //​Свойство декорировано атрибутом Browsable чтобы исключить отображение в инспекторе объектов 
 +        [Browsable(false)] 
 +        public object Owner 
 +        { 
 +            get 
 +            { 
 +                return m_Owner; 
 +            } 
 +            set 
 +            { 
 +                //​Владелец назначается один раз на конструкторе объекта,​ назначение отдельно недопустимо 
 +                throw new NotSupportedException();​ 
 +            } 
 +        } 
 + 
 +        //​Реализация загрузки 
 +        public void LoadFromStg(StgNode node) 
 +        { 
 +            //​обратите внимание,​ загрузку мы производим во внутренний список 
 +            //это позволяет избежать записи значений в историю изменений 
 +            m_Points.InnerList.Clear();​ 
 +            //При загрузке массива указывается тип составляющих массив значений 
 +            var array = node.GetArray("​Values",​ StgType.Node);​ 
 +            for (int i = 0; i < array.Count;​ i++) 
 +            { 
 +                m_Points.InnerList.Add(Vector2D.LoadFromStg(array.GetNode(i)));​ 
 +            } 
 +        } 
 + 
 +        //​Реализация сохранения 
 +        public void SaveToStg(StgNode node) 
 +        { 
 +            //​Сохраняем значения в узел 
 +            //​Сохраняем массив с указанием типа значений 
 +            var array = node.AddArray("​Values",​ StgType.Node);​ 
 +            for (int i = 0; i < m_Points.Count;​ i++) 
 +            { 
 +                m_Points[i].SaveToStg(array.AddNode());​ 
 +            } 
 +        } 
 + 
 +        //​Дополнительно перекрываем метод ToString() для отображения типа объекта в инстпекторе объектов 
 +        public override string ToString() 
 +        { 
 +            return "​Точки модели";​ 
 +        } 
 + 
 +        //​Привязка ​ КОНЕЧНАЯ ТОЧКА 
 +        public void GetEndPoint(ObjectsDisjointerArgs e, IList<​Vector3D>​ list) 
 +        { 
 +            for (int i = 0; i < m_Points.Count;​ i++) 
 +            { 
 +                list.Add(m_Points[i]);​ 
 +            } 
 +        } 
 + 
 +        //​Привязка ​ ЦЕНТРАЛЬНАЯ ТОЧКА 
 +        public void GetCenterPoint(ObjectsDisjointerArgs e, IList<​Vector3D>​ list) 
 +        { 
 +            //Do nothing 
 +        } 
 + 
 +        //​Привязка ​ СЕРЕДИНА ОТРЕЗКА 
 +        public void GetMiddlePoint(ObjectsDisjointerArgs e, IList<​Vector3D>​ list) 
 +        { 
 +            for (int i = 1; i < m_Points.Count;​ i++) 
 +            { 
 +                list.Add((m_Points[i - 1] + m_Points[i]) * 0.5); 
 +            } 
 +        } 
 + 
 +        //​Привязка ​ УЗЕЛ 
 +        public void GetNodePoint(ObjectsDisjointerArgs e, IList<​Vector3D>​ list) 
 +        { 
 +            //Do nothing 
 +        } 
 + 
 +        //​Привязка ​ КВАДРАНТ 
 +        public void GetQuadrantPoint(ObjectsDisjointerArgs e, IList<​Vector3D>​ list) 
 +        { 
 +            //Do nothing 
 +        } 
 + 
 +        //​Привязка ​ ТОЧКА ВСТАВКИ 
 +        public void GetInsertionPoint(ObjectsDisjointerArgs e, IList<​Vector3D>​ list) 
 +        { 
 +            //Do nothing 
 +        } 
 + 
 +        //​Привязка ​ БЛИЖАЙШАЯ ТОЧКА, КАСАТЕЛЬНАЯ и т.п. 
 +        public void GetSegments(ObjectsDisjointerArgs e, IList<​ArcSegment>​ arcList, IList<​LineSegment>​ lineList) 
 +        { 
 +            for (int i = 1; i < m_Points.Count;​ i++) 
 +            { 
 +                lineList.Add( new LineSegment() { StartPoint = m_Points[i - 1], EndPoint = m_Points[i] } ); 
 +            } 
 +        } 
 +    } 
 +</​code>​ 
 +Также изменяем Model.cs. Список содержащий экземпляры класса Points заменяем на [[developers:​references:​topomatic.foundationclasses.undo.basetransactablelist_1|BaseTransactableList<​T>​]] 
 +<code csharp>​ 
 +class Model : StateControllerObject,​ IStgSerializable 
 +    { 
 +        private bool m_ReadOnly = false; 
 + 
 +        //​Список объектов точек с поддержкой истории изменений 
 +        private BaseTransactableList<​Points>​ m_Points; 
 + 
 +        public Model() 
 +        { 
 +            m_Points = new BaseTransactableList<​Points>​(this);​ 
 +        } 
 + 
 +        //​Метод для добавления списка точек в модель 
 +        public Points Add() 
 +        { 
 +            var p = new Points(this);​ 
 +            m_Points.Add(p);​ 
 +            return p; 
 +        } 
 + 
 +        //​Метод для получения списка точек из модели 
 +        public Points this[int index] 
 +        { 
 +            get 
 +            { 
 +                return m_Points[index];​ 
 +            } 
 +        } 
 + 
 +        //​Метод для удаления списка точек из модели 
 +        public bool Remove(Points points) 
 +        { 
 +            return m_Points.Remove(points);​ 
 +        } 
 + 
 +        //​Количество списков в модели 
 +        public int Count 
 +        { 
 +            get 
 +            { 
 +                return m_Points.Count;​ 
 +            } 
 +        } 
 + 
 +        //флаг только для чтения 
 +        public override bool ReadOnly 
 +        { 
 +            get 
 +            { 
 +                return m_ReadOnly;​ 
 +            } 
 + 
 +            set 
 +            { 
 +                m_ReadOnly = value; 
 +            } 
 +        } 
 + 
 +        //​Загрузка из узла 
 +        public void LoadFromStg(StgNode node) 
 +        { 
 +            m_Points.InnerList.Clear();​ 
 +            //При загрузке массива указывается тип составляющих массив значений 
 +            var array = node.GetArray("​Points",​ StgType.Node);​ 
 +            for (int i = 0; i < array.Count;​ i++) 
 +            { 
 +                var p = new Points(this);​ 
 +                p.LoadFromStg(array.GetNode(i));​ 
 +                m_Points.InnerList.Add(p);​ 
 +            } 
 +        } 
 + 
 +        //​Сохранение в узел 
 +        public void SaveToStg(StgNode node) 
 +        { 
 +            //​Сохраняем значения в узел 
 +            //​Сохраняем массив с указанием типа значений 
 +            var array = node.AddArray("​Points",​ StgType.Node);​ 
 +            for (int i = 0; i < m_Points.Count;​ i++) 
 +            { 
 +                m_Points[i].SaveToStg(array.AddNode());​ 
 +            } 
 +        } 
 +    } 
 +</​code>​ 
 + 
 +Также вносим изменения в Module.cs в функцию редактирования модели. Теперь перед изменением значений мы вызываем метод [[developers:​references:​topomatic.foundationclasses.iupdatable.beginupdate|BeginUpdate]] а после метод [[developers:​references:​topomatic.foundationclasses.iupdatable.beginupdate|EndUpdate]]. Кроме того теперь нет необходимости выставлять свойство [[developers:​references:​topomatic.foundationclasses.statecontrollerobject.modified|Modified]] вручную,​ при изменении истории значение свойства определяется автоматически. 
 + 
 +<code csharp>​ 
 +        [cmd("​edit_pointsmodel"​)] 
 +        public void EditModel() 
 +        { 
 +            var cadView = CadView; 
 +            if (cadView != null) 
 +            { 
 +                var model_layer = ModelLayer.GetModelLayer(cadView);​ 
 +                if (model_layer != null) 
 +                { 
 +                    //​список выбранных точек 
 +                    var positions = new List<​Vector2D>​();​ 
 +                    //​делегат для динамической отрисовки 
 +                    DrawCursorEvent dynamic_draw = delegate (CadPen pen, Vector3D vertex) 
 +                    { 
 +                        //если есть точки в списке,​ нужно их нарисовать и нарисовать линию 
 +                        //от последней выбранной точки, до текущего положения курсора 
 +                        if (positions.Count > 0) 
 +                        { 
 +                            pen.Color = Color.Lime;​ 
 +                            pen.BeginDraw();​ 
 +                            try 
 +                            { 
 +                                for (int i = 1; i < positions.Count;​ i++) 
 +                                { 
 +                                    pen.DrawLine(positions[i - 1], positions[i]);​ 
 +                                } 
 +                                pen.DrawLine(positions[positions.Count - 1], vertex.Pos);​ 
 +                            } 
 +                            finally 
 +                            { 
 +                                pen.EndDraw();​ 
 +                            } 
 +                        } 
 +                    }; 
 +                    //​подписываемся на событие отрисовки 
 +                    cadView.DynamicDraw += dynamic_draw;​ 
 +                    try 
 +                    { 
 +                        Vector3D pos; 
 +                        //​просим пользователя указать несколько точек 
 +                        while (CadCursors.GetPoint(cadView,​ out pos, "​Укажите точку"​)) 
 +                        { 
 +                            positions.Add(pos.Pos);​ 
 +                        } 
 +                        //если точки заданы,​ то изменяем нашу модель 
 +                        if (positions.Count > 0) 
 +                        { 
 +                            //​получаем её со слоя 
 +                            var model = model_layer.Model;​ 
 +                            //​начинаем групповое изменение модели 
 +                            model.BeginUpdate();​ 
 +                            try 
 +                            { 
 +                                //​добавляем новые точки 
 +                                var points = model.Add();​ 
 +                                for (int i = 0; i < positions.Count;​ i++) 
 +                                { 
 +                                    points.AddPoint(positions[i]);​ 
 +                                } 
 +                            } 
 +                            finally 
 +                            { 
 +                                //​заканчиваем групповое изменение модели 
 +                                model.EndUpdate();​ 
 +                            } 
 +                            //​обновляем видовой экран 
 +                            cadView.Unlock();​ 
 +                            cadView.Invalidate();​ 
 +                        } 
 +                    } 
 +                    finally 
 +                    { 
 +                        //​отписываемся от события отрисовки 
 +                        cadView.DynamicDraw -= dynamic_draw;​ 
 +                    } 
 +                } 
 +            } 
 +        } 
 +</​code>​ 
 + 
 +В результате мы получим возможность использовать историю изменений и отменять изменения в нашей модели с помощью этого механизма. 
 + 
 +<​note>​[[developers:​tutorial:​tutorialcode|Исходный код]] примера расположен в проекте **"​tutorial10"​**.</​note>​ 
developers/tutorial/undoredo.1553372872.txt.gz · Последние изменения: 2021/07/22 14:28 (внешнее изменение)