Привязки и ручки перемещения

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

  • Наследники от класса Grip - предназначены для визуального изменения конфигурации, например перемещение начала отрезка из одной точки в другую
  • Наследники от класса ClickGrip - предназначены для вызова функций без визуального отображения
Наследники от класса ClickGrip обычно используются в качестве элементов, вложенных в Grip для формирования контекстного меню дополнительных команд, не требующих визуального отображения

Для возвращения списка ручек перемещения отвечает метод GetObjectGrips класса SelectionSet.

Для реализации собственной ручки перемещения необходимо:

  • Создать наследника от Grip
  • Используя свойство Location указать положение ручки перемещения
  • При помощи метода AddGrip сформировать дополнительное контекстное меню, если это необходимо
  • Перекрыть метод OnDynamicRender и реализовать визуальное отображение при перемещении курсора
  • Перекрыть метод OnMove и реализовать изменение конфигурации объекта после перемещения ручки
Управлять формой ручки перемещения можно используя свойства GripType и Rotation или перекрыв метод OnPaint

За реализацию объектных привязок отвечает интерфейс IObjectDisjoiner:

  • GetCenterPoint - привязка к центральной точке
  • GetEndPoint- привязка к конечной точке
  • GetInsertionPoint - привязка к точке вставки
  • GetMiddlePoint - привязка к середине
  • GetNodePoint - привязка к узлу
  • GetQuadrantPoint - привязка к квадранту
  • GetSegments - остальные типы привязок, такие как к ближайшей точке, нормаль, касательная и т.п.

Для возвращения объектных привязок необходимо перекрыть метод OnGetSnapObjects класса CadViewLayer.

Создайте новую модель и видовой слой и подключите к программному комплексу Топоматик Робур.

Создайте класс 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. В нём мы поддержим возможность удаления и изменения вершины, а также поддержим реализацию интерфейса IObjectDisjoiner:

//Класс для хранения точек в нашей модели
    //поддерживаем интерфейсы IStgSerializable для сохранения и IOwned для определения владельца списка точек
    //Также поддерживаем интерфейс IObjectDisjoiner для привязок
    class Points : IStgSerializable, IOwned, IObjectDisjoiner
    {
        private Model m_Owner;
 
        private List<Vector2D> m_Points = new List<Vector2D>();
 
        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<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] } );
            }
        }
    }

В слое модели, расположенном в файле ModelLayer.cs, реализуем поддержку объектных привязок.

...
            for (int i = 0; i < m_Model.Count; i++)
            {
                //возвращаем реализации интерфейса IObjectDisjoiner
                e.SnapObjects.Add(m_Model[i]);
            }
...

А в классе ModelLayerSelectionSet, реализуем поддержку ручек перемещения.

...
        public override IEnumerable<IGrip> 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);
                }
            }
        }
...

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

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

Архив с кодом примера

developers/tutorial/gripsandsnaps.txt · Последние изменения: 2021/07/22 14:29 (внешнее изменение)