====== Выделение элементов ======
За выделение объектов на видовом экране отвечает класс, наследник от абстрактного класса [[developers:references:topomatic.cad.view.selectionset|SelectionSet]]. Он за возвращение списка всех выделяемых элементов, выделение элементов и их хранение, а также за копирование элементов через буфер обмена и их перемещение, если это необходимо. Каждый [[[[developers:references:topomatic.cad.view.cadviewlayer|слой]] видового экрана при реализации возвращает свой экземпляр [[developers:references:topomatic.cad.view.selectionset|SelectionSet]].
Если [[[[developers:references:topomatic.cad.view.cadviewlayer|слой]] не поддерживает выделение элементов, то в качестве [[developers:references:topomatic.cad.view.selectionset|SelectionSet]] можно вернуть экземпляр [[developers:references:topomatic.cad.view.defaultselectionset|DefaultSelectionSet]]. Это реализация по умолчанию, не позволяющая выделить элементы.
Для реализации наследника от [[developers:references:topomatic.cad.view.selectionset|SelectionSet]] необходимо реализовать следующие функции и методы:
* [[developers:references:topomatic.cad.view.selectionset.select_system.collections.ienumerable_system.boolean|Select]] - предназначен для выделения или снятия выделения с объекта.
* [[developers:references:topomatic.cad.view.selectionset.clear|Clear]] - снимает выделение со всех выделенных объектов
* [[developers:references:topomatic.cad.view.selectionset.erase|Erase]] - удаляет все выделенные объекты.
* [[developers:references:topomatic.cad.view.selectionset.getobjectsatpoint_topomatic.cad.foundation.vector3d_system.predicate_1_system.int32|GetObjectsAtPoint]] - выбор объектов в указанной точке и расстояние до них.
* [[developers:references:194476d8735a41d451648829001e3a70|GetObjectsByFrame]] - выбор объектов рамкой
* [[developers:references:f6f3e815a6f715eef33c195c169a8798|GetObjectsByPolygon]] - выбор объектов полигоном. В данный момент метод не поддерживается.
* [[developers:references:topomatic.cad.view.selectionset.getenumerator|GetEnumerator]] - перечисление выделенных объектов.
* [[developers:references:topomatic.cad.view.selectionset.getselectable|GetSelectable]] - перечисление всех объектов которые можно выделить.
* [[developers:references:topomatic.cad.view.selectionset.count|Count]] - количество выделенных элементов
* [[developers:references:topomatic.cad.view.selectionset.isenable_system.object|IsEnable]] - проверяет включен ли объект на этом [[developers:references:topomatic.cad.view.selectionset|SelectionSet]].
* [[developers:references:topomatic.cad.view.selectionset.isowned_system.object|IsOwned]] - проверяет принадлежит ли объект выделяемым объектам этого [[developers:references:topomatic.cad.view.selectionset|SelectionSet]].
* [[developers:references:topomatic.cad.view.selectionset.isselected_system.object|IsSelected]] - проверяет выделен ли объект на этом [[developers:references:topomatic.cad.view.selectionset|SelectionSet]].
В функцию [[developers:references:topomatic.cad.view.selectionset.getobjectsatpoint_topomatic.cad.foundation.vector3d_system.predicate_1_system.int32|GetObjectsAtPoint]] в качестве параметра приходит временной интервал, в течении которого вы должны завершить выполнение функции. Если интервал равен 0 то время на выполнение функции не ограничено.
Свойства объектов, которые будут выделены с помощью [[developers:references:topomatic.cad.view.selectionset|SelectionSet]] будут отображены на видовом экране. А в качестве типа объекта будет выведено значение функции [[developers:references:system.object.tostring|ToString()]].
Для того чтобы управлять названием и положением свойства в инспекторе объектов можно использовать атрибуты [DisplayName] и [Category]. Для скрытия свойства доступен атрибут [Browsable]
Более подробно работа со свойствами описана в разделе [[developers:tutorial:dlgandpropertygrid|Таблицы и диалоги]].
Для реализации методов для выбора объектов, таких как [[developers:references:topomatic.cad.view.selectionset.getobjectsatpoint_topomatic.cad.foundation.vector3d_system.predicate_1_system.int32|GetObjectsAtPoint]] и сложной геометрии объектов можно использовать специальный класс - [[developers:references:topomatic.cad.view.nulldevicecontext|NullDeviceContext]]. Он позволяет определить принадлежит ли заданная точка объекту или рамке, нарисовав сам объект в памяти.
При реализации метода [[developers:references:194476d8735a41d451648829001e3a70|GetObjectsByFrame]] необходимо учитывать тип рамки, и в зависимости от этого пользоваться либо [[developers:references:topomatic.cad.view.nulldevicecontext|NullDeviceContext]] либо [[developers:references:topomatic.cad.view.fulldevicecontext|FullDeviceContext]].
Создайте новую [[developers:tutorial:addlayer|модель и видовой слой]] и подключите к программному комплексу [[http://www.topomatic.ru|Топоматик Робур]].
Изменим модель таким образом, чтобы она хранила внутри несколько списков точек.
Для этого создадим класс Points.cs описывающий список точек:
//Класс для хранения точек в нашей модели
//поддерживаем интерфесы IStgSerializable для сохранения и IOwned для определения владельца списка точек
class Points : IStgSerializable, IOwned
{
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 Vector2D this[int index]
{
get
{
return m_Points[index];
}
}
//Количество точек в списке
//Свойство декорировано атрибутом 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 "Точки модели";
}
}
Поскольку экземпляры этого класса в дальнейшем будут выделятся на слое, то все свойства мы декорируем необходимыми атрибутами и дополнительно перекрываем метод [[developers:references:system.object.tostring|ToString()]].
Реализуем класс модели и поместим его в файл Model.cs:
//Класс реализующий структуру данных нашей модели
//для поддержки интерфейса IStateController наследуем от StateControllerObject
class Model : StateControllerObject, IStgSerializable
{
private bool m_ReadOnly = false;
private List m_Points = new List();
//Метод для добавления списка точек в модель
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.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.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());
}
}
}
Дополнительно создадим сервисный класс, реализующий отображение списка точек и поместим его в файл PointsDrawer.cs:
//Сервисный класс для отрисовки точек
static class PointsDrawer
{
//Функция рисующая список точек при помощи pen
public static void PaintPoints(CadPen pen, Points points, bool enabled)
{
//рисуем нашу линию жёлтым цветом
pen.Color = Color.Yellow;
//Начинаем рисовать
pen.BeginDraw();
try
{
//для отрисовки используем возможность нарисовать массив нескольких точек
//Для этого вызываем начало отрисовки массива
pen.BeginArray();
for (int i = 0; i < points.Count; i++)
{
//Добавляем точки
pen.Vertex(points[i]);
}
//Заканчиваем отрисовку массива, в виде линии
pen.EndArray(ArrayMode.Polyline);
}
finally
{
//заканчиваем рисовать
pen.EndDraw();
}
//в каждой точке пишем номер оранжевым цветом
pen.Color = Color.Orange;
//начинаем рисовать
pen.BeginDraw();
try
{
var font = FontManager.Current.DefaultFont;
for (int i = 0; i < points.Count; i++)
{
//пишем номер точки, высотой в 2 еденицы чертежа
font.DrawString(i.ToString(), pen, points[i], 0.0, 2.0);
}
}
finally
{
//заканчиваем рисовать
pen.EndDraw();
}
}
}
Реализуем нашего наследника от [[developers:references:topomatic.cad.view.selectionset|SelectionSet]] и разместим его в файле ModelLayerSelectionSet.cs:
class ModelLayerSelectionSet : SelectionSet
{
//Список выделенных объектов
private List
В реализацию слоя модели, расположенного в файле ModelLayer.cs, необходимо внести следующие изменения.
На конструкторе создаем экземпляра класса, наследника от [[developers:references:topomatic.cad.view.selectionset|SelectionSet]], меняем реализации функций **OnPaint** и **OnGetLimits**. Также перекрываем функции **OnHilightObject** и **OnDynamicDraw** - для реализации подсветки и отображения выделенных элементов.
...
public ModelLayer()
{
//в качестве класса, отвечающего за выделение объектов мы используем ModelLayerSelectionSet
m_SelectionSet = new ModelLayerSelectionSet(this);
}
...
protected override bool OnGetLimits(out BoundingBox2D limits)
{
bool init = false;
limits = BoundingBox2D.Empty;
for (int i = 0; i < m_Model.Count; i++)
{
var points = m_Model[i];
for (int j = 0; j < points.Count; j++)
{
var v = points[j];
if (init)
{
limits.AddPoint(v);
}
else
{
limits = new BoundingBox2D(v, v);
init = true;
}
}
}
return init;
}
...
//Отрисовка модели
protected override void OnPaint(CadPen pen)
{
for (int i = 0; i < m_Model.Count; i++)
{
PointsDrawer.PaintPoints(pen, m_Model[i], ResolveEnable());
}
}
...
//Отрисовка подсвеченных объектов
protected override void OnHilightObject(CadPen pen, object obj)
{
base.OnHilightObject(pen, obj);
//Если объект это наши точки
var points = obj as Points;
if (points != null)
{
//То включаем режим подсветки
pen.DrawingMode = DrawingMode.Highlight;
pen.HighlightMode = HighlightMode.DoubleBlack;
//Рисуем точки
PointsDrawer.PaintPoints(pen, points, ResolveEnable());
pen.Reset();
}
}
...
//Динамическая отрисовка слоя
protected override void OnDynamicDraw(CadPen pen, Vector3D location)
{
base.OnDynamicDraw(pen, location);
//Если есть выбранные элементы
if (m_SelectionSet.Count > 0)
{
//Необходимо нарисовать все выбранные элементы
foreach (var obj in m_SelectionSet)
{
var points = obj as Points;
if (points != null)
{
//Рисуем элементы подсвеченными
pen.HighlightMode = HighlightMode.Black;
PointsDrawer.PaintPoints(pen, points, ResolveEnable());
pen.Reset();
}
}
}
}
...
В результате мы получим возможность по команде «Редактировать активную модель» добавлять новые полилинии с точками, а также выделять и подсвечивать их на видовом экране.
{{ :developers:tutorial:selectionset:selectionset_result.png?direct&600 |}}
[[developers:tutorial:tutorialcode|Исходный код]] примера расположен в проекте **"tutorial8"**.