====== Привязки и ручки перемещения ====== Ручки перемещения предназначены для изменения конфигурации выделенных объектов. Каждый объект может содержать произвольное количество ручек перемещения, разного вида и конфигурации. Ручки перемещения подразделяются на два типа: * Наследники от класса [[developers:references:topomatic.cad.view.grip|Grip]] - предназначены для визуального изменения конфигурации, например перемещение начала отрезка из одной точки в другую * Наследники от класса [[developers:references:topomatic.cad.view.clickgrip|ClickGrip]] - предназначены для вызова функций без визуального отображения Наследники от класса [[developers:references:topomatic.cad.view.clickgrip|ClickGrip]] обычно используются в качестве элементов, вложенных в [[developers:references:topomatic.cad.view.grip|Grip]] для формирования контекстного меню дополнительных команд, не требующих визуального отображения Для возвращения списка ручек перемещения отвечает метод [[developers:references:topomatic.cad.view.selectionset.getobjectgrips_system.object|GetObjectGrips]] класса [[developers:references:topomatic.cad.view.selectionset|SelectionSet]]. Для реализации собственной ручки перемещения необходимо: * Создать наследника от [[developers:references:topomatic.cad.view.grip|Grip]] * Используя свойство [[developers:references:topomatic.cad.view.grip.location|Location]] указать положение ручки перемещения * При помощи метода [[developers:references:topomatic.cad.view.grip.addgrip_system.string_system.string_topomatic.cad.view.igrip|AddGrip]] сформировать дополнительное контекстное меню, если это необходимо * Перекрыть метод [[developers:references:topomatic.cad.view.grip.ondynamicrender_topomatic.cad.foundation.devicecontext_topomatic.cad.foundation.vector3d|OnDynamicRender]] и реализовать визуальное отображение при перемещении курсора * Перекрыть метод [[developers:references:topomatic.cad.view.grip.onmove_topomatic.cad.foundation.vector3d|OnMove]] и реализовать изменение конфигурации объекта после перемещения ручки Управлять формой ручки перемещения можно используя свойства [[developers:references:topomatic.cad.view.grip.griptype|GripType]] и [[developers:references:topomatic.cad.view.grip.rotation|Rotation]] или перекрыв метод [[developers:references:topomatic.cad.view.grip.onpaint_topomatic.cad.view.paintgripeventargs|OnPaint]] За реализацию [[road:startup_and_setting_topomatic:general_setting:regime_drawing:start|объектных привязок]] отвечает интерфейс [[developers:references:topomatic.cad.foundation.iobjectdisjoiner|IObjectDisjoiner]]: * [[developers:references:topomatic.cad.foundation.iobjectdisjoiner.getcenterpoint_topomatic.cad.foundation.objectsdisjointerargs_system.collections.generic.ilist_1|GetCenterPoint]] - привязка к центральной точке * [[developers:references:topomatic.cad.foundation.iobjectdisjoiner.getendpoint_topomatic.cad.foundation.objectsdisjointerargs_system.collections.generic.ilist_1|GetEndPoint]]- привязка к конечной точке * [[developers:references:topomatic.cad.foundation.iobjectdisjoiner.getinsertionpoint_topomatic.cad.foundation.objectsdisjointerargs_system.collections.generic.ilist_1|GetInsertionPoint]] - привязка к точке вставки * [[developers:references:topomatic.cad.foundation.iobjectdisjoiner.getmiddlepoint_topomatic.cad.foundation.objectsdisjointerargs_system.collections.generic.ilist_1|GetMiddlePoint]] - привязка к середине * [[developers:references:topomatic.cad.foundation.iobjectdisjoiner.getnodepoint_topomatic.cad.foundation.objectsdisjointerargs_system.collections.generic.ilist_1|GetNodePoint]] - привязка к узлу * [[developers:references:topomatic.cad.foundation.iobjectdisjoiner.getquadrantpoint_topomatic.cad.foundation.objectsdisjointerargs_system.collections.generic.ilist_1|GetQuadrantPoint]] - привязка к квадранту * [[developers:references:d02910a106e061a10b718dd70447cf16|GetSegments]] - остальные типы привязок, такие как к ближайшей точке, нормаль, касательная и т.п. Для возвращения [[road:startup_and_setting_topomatic:general_setting:regime_drawing:start|объектных привязок]] необходимо перекрыть метод **OnGetSnapObjects** класса [[developers:references:topomatic.cad.view.cadviewlayer|CadViewLayer]]. Создайте новую [[developers:tutorial:selectionset|модель и видовой слой]] и подключите к программному комплексу [[http://www.topomatic.ru|Топоматик Робур]]. Создайте класс PointGrip.cs и разместите в нём реализацию ручки перемещения. //Реализация грипа для перемещения вершины class PointGrip : Grip { private Points m_Points; private int m_Index; //Конструктор public PointGrip(CadView cadview, Points points, int index) : base(cadview) { m_Index = index; m_Points = points; this.Location = m_Points[index]; //Дополнительный грип для удаления вершины var g = new ClickGrip(); g.Click += delegate { //Удаляем вершину m_Points.RemovePoint(m_Index); //Обновляем видовой экран CadView.Unlock(); CadView.Invalidate(); }; //Добавляем дополнительный грип this.AddGrip("Удалить", "remove_point", g); } //Реализация применения изменений public override void OnMove(Vector3D vertex) { base.OnMove(vertex); //Назначаем нашей точке новое положение m_Points[m_Index] = vertex.Pos; } //Реализация динамической отрисовки public override void OnDynamicRender(DeviceContext dc, Vector3D position) { base.OnDynamicRender(dc, position); //Если наша точка не первая, то рисуем линию до предыдущей точки if (m_Index > 0) { AuxiliaryDrawer.DrawAuxiliaryLine3d(dc, m_Points[m_Index - 1], position); } //Если наша точка не последняя, то рисуем линию до следующей точки if (m_Index < m_Points.Count - 1) { AuxiliaryDrawer.DrawAuxiliaryLine3d(dc, m_Points[m_Index + 1], position); } } } Дополнительно измените класс Points.cs. В нём мы поддержим возможность удаления и изменения вершины, а также поддержим реализацию интерфейса [[developers:references:topomatic.cad.foundation.iobjectdisjoiner|IObjectDisjoiner]]: //Класс для хранения точек в нашей модели //поддерживаем интерфейсы IStgSerializable для сохранения и IOwned для определения владельца списка точек //Также поддерживаем интерфейс IObjectDisjoiner для привязок class Points : IStgSerializable, IOwned, IObjectDisjoiner { private Model m_Owner; private List m_Points = new List(); public Points(Model owner) { m_Owner = owner; } //Метод для добавления точки в список 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.Clear(); //При загрузке массива указывается тип составляющих массив значений var array = node.GetArray("Values", StgType.Node); for (int i = 0; i < array.Count; i++) { m_Points.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 list) { for (int i = 0; i < m_Points.Count; i++) { list.Add(m_Points[i]); } } //Привязка ЦЕНТРАЛЬНАЯ ТОЧКА public void GetCenterPoint(ObjectsDisjointerArgs e, IList list) { //Do nothing } //Привязка СЕРЕДИНА ОТРЕЗКА public void GetMiddlePoint(ObjectsDisjointerArgs e, IList 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 list) { //Do nothing } //Привязка КВАДРАНТ public void GetQuadrantPoint(ObjectsDisjointerArgs e, IList list) { //Do nothing } //Привязка ТОЧКА ВСТАВКИ public void GetInsertionPoint(ObjectsDisjointerArgs e, IList list) { //Do nothing } //Привязка БЛИЖАЙШАЯ ТОЧКА, КАСАТЕЛЬНАЯ и т.п. public void GetSegments(ObjectsDisjointerArgs e, IList arcList, IList lineList) { for (int i = 1; i < m_Points.Count; i++) { lineList.Add( new LineSegment() { StartPoint = m_Points[i - 1], EndPoint = m_Points[i] } ); } } } В слое модели, расположенном в файле ModelLayer.cs, реализуем поддержку [[road:startup_and_setting_topomatic:general_setting:regime_drawing:start|объектных привязок]]. ... for (int i = 0; i < m_Model.Count; i++) { //возвращаем реализации интерфейса IObjectDisjoiner e.SnapObjects.Add(m_Model[i]); } ... А в классе ModelLayerSelectionSet, реализуем поддержку ручек перемещения. ... public override IEnumerable GetObjectGrips(object obj) { var points = obj as Points; if (points != null) { var cadView = Layer.CadView; //для каждой точки возвращаем ручку перемещения для её редактирования for (int i = 0; i < points.Count; i++) { yield return new PointGrip(cadView, points, i); } } } ... В результате мы получим возможность редактировать точки линии при помощи ручек перемещения, а также привязываться к линии с помощью механизма [[road:startup_and_setting_topomatic:general_setting:regime_drawing:start|объектных привязок]]. {{ :developers:tutorial:gripsandsnaps:gripsandsnaps_result.png?direct&600 |}} Исходный код примера, вы можете скачать используя эту ссылку {{ :developers:tutorial:gripsandsnaps:tutorial9.zip |Архив с кодом примера}}