В связи с производственной необходимостью, возникла потребность в компоненте для построения графов. Сходу были найдены вот эти три проекта:
http://graphx.codeplex.com/
http://graphsharp.codeplex.com/
http://nodexl.codeplex.com/
Т.к. по картинкам мне больше понравился третий, то его и пробовал. Он оказался неплох. Поэтому под катом рассказ о том, как при помощи NodeXL строить графики в WPF приложениях.
На всякий пожарный ссылка на скачивание. Качаем, распакуем, присоединяем dll-ки в проект:
Все, можно начинать использовать.
Для показа графа используется контрол NodeXLControl из пространства имен Smrf.NodeXL.Visualization.Wpf. Можно его как добавить через XAML, так и создать из кода и поместить в какой-нибудь контейнер. Дополнительных настроек не требуется.
Для данной статьи, я создал пустой WPF проект и на главную форму поместил вот такую разметку:
Ну а теперь, собственно построение графа. Информация о графе собрана в свойстве с говорящим именем Gpaph. Ну а вершины, соответственно, в свойстве Vertices графа. Чтобы постоянно не писать весь путь с имени контрола, можно это свойство скопировать в переменную и работать с ней:
Интерфейс у компонента достаточно понятный, например, добавление вершин осуществляется методом Add возвращающем ссылку на свежесозданную вершину:
А вот дальше начинается проблема из-за желания разработчика сделать все максимально универсально. Настройка свойств вершины идет через метод SetValue первым параметром в который необходимо передавать строку с именем настраиваемого свойства. Сильно радует наличие предопределенного класса ReservedMetadataKeys с перечнем этих строковых значений в виде readonly полей. Т.е. настройка вершины имеет вид:
Ну и еще пара вершин оформленных по другому:
Мы можем задавать расположение вершин принудительно. У каждой вершины есть свойство Location, вот только проблема, это свойство из библиотеки WinForms. Нет, мы можем подключить бибилотеку System.Drawing.dll и написать что-нибудь вида:
third.Location = new System.Drawing.PointF(60, 60);
И оно даже заработает:
Но в большинстве случаев, мы можем просто при построении графа довериться авторасположению вершин:
nxGraph.DrawGraph(true);
Выглядит:
Только нужно не забывать, что при выборе такого способа все установленные вручную Location будут игнорироваться.
Хорошо, вершины разместили, переходим к ребрам. Список всех ребер лежит в свойстве Edges уже упоминавшегося свойства Graph:
Работа с ребрами очень похожа на работу с вершинами, только при создании ребра мы передаем две вершины которые ребро будет соединять и признак направленности ребра:
Компонент представляет возможность группировать вершины, но смотрится это не фонтан, т.к. все связи к сгруппированным вершинам отображаются. Но давайте покажу.
Поэтому предлагаю перейти к событиям и на них покажу работу с группами.
Событий у вершин, групп и т.д. нет. Все события собраны в контроле. Например, чтобы обрабатывать клик на первой или второй вершине со сворачиванием группы мне придется подписаться на событие:
Вот так выглядит граф после сворачивания двух вершин:
На сегодня все. Может в следующий раз покажу какой-нибудь пример приближенный к реальности на основе этого контрола.
http://graphx.codeplex.com/
http://graphsharp.codeplex.com/
http://nodexl.codeplex.com/
Т.к. по картинкам мне больше понравился третий, то его и пробовал. Он оказался неплох. Поэтому под катом рассказ о том, как при помощи NodeXL строить графики в WPF приложениях.
На всякий пожарный ссылка на скачивание. Качаем, распакуем, присоединяем dll-ки в проект:
Все, можно начинать использовать.
Для показа графа используется контрол NodeXLControl из пространства имен Smrf.NodeXL.Visualization.Wpf. Можно его как добавить через XAML, так и создать из кода и поместить в какой-нибудь контейнер. Дополнительных настроек не требуется.
Для данной статьи, я создал пустой WPF проект и на главную форму поместил вот такую разметку:
<Window x:Class="WpfApplication9.MainWindow"
xmlns:node="clr-namespace:Smrf.NodeXL.Visualization.Wpf;assembly=Smrf.NodeXL.Control.Wpf"
Title="MainWindow" Height="350" Width="525">
<Grid>
<node:NodeXLControl x:Name="nxGraph" />
</Grid>
</Window>
Ну а теперь, собственно построение графа. Информация о графе собрана в свойстве с говорящим именем Gpaph. Ну а вершины, соответственно, в свойстве Vertices графа. Чтобы постоянно не писать весь путь с имени контрола, можно это свойство скопировать в переменную и работать с ней:
IVertexCollection oVertices = nxGraph.Graph.Vertices;
Интерфейс у компонента достаточно понятный, например, добавление вершин осуществляется методом Add возвращающем ссылку на свежесозданную вершину:
IVertex first = oVertices.Add();
// Цвет вершины
first.SetValue(ReservedMetadataKeys.PerVertexLabelFillColor, Color.FromArgb(255, 255, 255, 11));
// Тип отображения
first.SetValue(ReservedMetadataKeys.PerVertexShape, VertexShape.Label);
// Текст вершины
first.SetValue(ReservedMetadataKeys.PerVertexLabel, "Первая");
Да, вы правильно поняли, что второй параметр метода SetValue типа object и во многих случаях придется угадывать какого типа он должен быть реально по замыслу разработчиков.Ну и еще пара вершин оформленных по другому:
IVertex second = oVertices.Add();
second.SetValue(ReservedMetadataKeys.PerColor, Color.FromArgb(255, 255, 0, 255));
// Задаем радиус вершины
second.SetValue(ReservedMetadataKeys.PerVertexRadius, 20F);
// И говорим что вершина - шарик
second.SetValue(ReservedMetadataKeys.PerVertexShape, VertexShape.Sphere);
IVertex third = oVertices.Add();
// Здесь мы тоже говорим, что сфера
third.SetValue(ReservedMetadataKeys.PerVertexShape, VertexShape.Sphere);
// Но задаем надпись
third.SetValue(ReservedMetadataKeys.PerVertexLabel, "Label");
Ок, давайте запустим наше приложение. Но перед этим вызовем метод отрисовки графа:nxGraph.DrawGraph();
Вот так это выглядит:Мы можем задавать расположение вершин принудительно. У каждой вершины есть свойство Location, вот только проблема, это свойство из библиотеки WinForms. Нет, мы можем подключить бибилотеку System.Drawing.dll и написать что-нибудь вида:
third.Location = new System.Drawing.PointF(60, 60);
И оно даже заработает:
Но в большинстве случаев, мы можем просто при построении графа довериться авторасположению вершин:
nxGraph.DrawGraph(true);
Выглядит:
Только нужно не забывать, что при выборе такого способа все установленные вручную Location будут игнорироваться.
Хорошо, вершины разместили, переходим к ребрам. Список всех ребер лежит в свойстве Edges уже упоминавшегося свойства Graph:
IEdgeCollection oEdges = nodeXLControl.Graph.Edges;
Работа с ребрами очень похожа на работу с вершинами, только при создании ребра мы передаем две вершины которые ребро будет соединять и признак направленности ребра:
IEdge oEdge1 = oEdges.Add(first, second, true);
Ну а свойства уже по привычной схеме:// Цвет
oEdge1.SetValue(ReservedMetadataKeys.PerColor, Color.FromArgb(255, 55, 125, 98));
// Толщина
oEdge1.SetValue(ReservedMetadataKeys.PerEdgeWidth, 3F);
// Подпись
oEdge1.SetValue(ReservedMetadataKeys.PerEdgeLabel, "Из первой во вторую");
Аналогично для остальных вершин:// Первую и третью вершину соеденит ненаправленное ребро
IEdge oEdge2 = oEdges.Add(first, third, false);
// Цвет
oEdge1.SetValue(ReservedMetadataKeys.PerColor, Color.FromArgb(255, 55, 125, 98));
// Вторую с третьей - направленное
IEdge oEdge3 = oEdges.Add(second, third, true);
// Линия будет штрих-пунктирная
oEdge3.SetValue(ReservedMetadataKeys.PerEdgeStyle, EdgeStyle.DashDotDot);
Смотрится симпотично:Компонент представляет возможность группировать вершины, но смотрится это не фонтан, т.к. все связи к сгруппированным вершинам отображаются. Но давайте покажу.
// Создаем группу, по умолчанию свернутую (второй параметр)
GroupInfo oGroup = new GroupInfo("GroupFirstAndSecond", true, "Тут две вершины");
// Вершины объединенные группой
oGroup.Vertices.AddFirst(first);
oGroup.Vertices.AddLast(second);
// Добавляем группу в граф
nxGraph.Graph.SetValue(ReservedMetadataKeys.GroupInfo, new GroupInfo[] { oGroup });
Обратили внимание на признак свернутости? Так вот, он игнорируется... Если сейчас запустить, то мы увидим все тоже самое, что и на предыдущей картинке.Поэтому предлагаю перейти к событиям и на них покажу работу с группами.
Событий у вершин, групп и т.д. нет. Все события собраны в контроле. Например, чтобы обрабатывать клик на первой или второй вершине со сворачиванием группы мне придется подписаться на событие:
nxGraph.VertexClick += nxGraph_VertexClick;
И в обработчике добавить проверку:void nxGraph_VertexClick(object sender, VertexEventArgs vertexEventArgs)
{
if (vertexEventArgs.Vertex.ID < 3) // Первая или вторая вершина
{
Dispatcher.BeginInvoke((Action)(() => nxGraph.CollapseGroup("GroupFirstAndSecond", true)));
}
if (vertexEventArgs.Vertex.ID == 3)
{
nxGraph.ExpandGroup("GroupFirstAndSecond", true);
}
}
Вызов через Dispatcher вынужденная мера, т.к. после сворачивания вершины из графа скрываются и обработка клика падает.Вот так выглядит граф после сворачивания двух вершин:
На сегодня все. Может в следующий раз покажу какой-нибудь пример приближенный к реальности на основе этого контрола.
Комментариев нет:
Отправить комментарий