====== Привязки и ручки перемещения ======
Ручки перемещения предназначены для изменения конфигурации выделенных объектов. Каждый объект может содержать произвольное количество ручек перемещения, разного вида и конфигурации. Ручки перемещения подразделяются на два типа:
* Наследники от класса [[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 |Архив с кодом примера}}