В связи с производственной необходимостью, возникла потребность в компоненте для построения графов. Сходу были найдены вот эти три проекта:
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();
 

А вот дальше начинается проблема из-за желания разработчика сделать все максимально универсально. Настройка свойств вершины идет через метод SetValue первым параметром в который необходимо передавать строку с именем настраиваемого свойства. Сильно радует наличие предопределенного класса ReservedMetadataKeys с перечнем этих строковых значений в виде readonly полей. Т.е. настройка вершины имеет вид:

// Цвет вершины
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 вынужденная мера, т.к. после сворачивания вершины из графа скрываются и обработка клика падает.
Вот так выглядит граф после сворачивания двух вершин:

На сегодня все. Может в следующий раз покажу какой-нибудь пример приближенный к реальности на основе этого контрола.