This topic provides a step by step guidance on how to bind the xamDiagram™ control to hierarchical node data.
The following topics are prerequisites to understanding this topic:
The following procedure demonstrates how to bind the xamDiagram to data where input data objects represent nodes and each of them has a collection of child node objects.
The following screenshot is a preview of the result.
To complete the procedure, you need the following:
A WPF application with an empty xamDiagram added to a page
The Layout property of the xamDiagram set to an instance of the ForceDirectedGraphDiagramLayout
Following is a conceptual overview of the process:
Configure the ItemsSource
property
Create the node definitions
Create the connection definitions
The following steps demonstrate how to bind the xamDiagram to hierarchical node data with references.
Add the sample data class
Add the following Employee
and Manager
classes to the code behind. The Manager
class has a list property with Employee
instances containing the manager’s subordinates. This property is used to create the Manager-Employee relations in the xamDiagram . Note that there is no requirement for the parent-child types to have any inheritance relation, and self-referencing types can be used too.
In C#:
public class Employee : INotifyPropertyChanged
{
public Employee(string name)
{
Name = name;
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Manager : Employee
{
public Manager(string name) : base(name) { }
private IList<Employee> _subordinates;
public IList<Employee> Subordinates
{
get { return _subordinates; }
set
{
_subordinates = value;
OnPropertyChanged();
}
}
}
In VB:
Public Class Employee
Implements INotifyPropertyChanged
Public Sub New(name As String)
Name = name
End Sub
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(value As String)
_name = value
OnPropertyChanged()
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(Optional propertyName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Public Class Manager
Inherits Employee
Public Sub New(name As String)
MyBase.New(name)
End Sub
Private _subordinates As IList(Of Employee)
Public Property Subordinates() As IList(Of Employee)
Get
Return _subordinates
End Get
Set(value As IList(Of Employee))
_subordinates = value
OnPropertyChanged()
End Set
End Property
End Class
Add the view model class.
Add the following view model class that the xamDiagram will be bound to:
In C#:
public class EmployeeViewModel : INotifyPropertyChanged
{
public EmployeeViewModel()
{
var employees = new ObservableCollection<Employee>();
var sharedEmployee = new Employee("Shared Employee");
employees.Add(new Manager("Manager 1")
{
Subordinates = new List<Employee>()
{
new Employee("Employee 1"),
new Employee("Employee 2"),
new Employee("Employee 3"),
new Employee("Employee 4"),
sharedEmployee
}
});
employees.Add(new Manager("Manager 2")
{
Subordinates = new List<Employee>()
{
sharedEmployee,
new Employee("Employee 5"),
new Employee("Employee 6"),
new Employee("Employee 7"),
new Employee("Employee 8"),
new Employee("Employee 9")
}
});
employees.Add(new Employee("Employee 10"));
Employees = employees;
}
private IList<Employee> _employees;
public IList<Employee> Employees
{
get { return _employees; }
set
{
_employees = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In VB:
Public Class EmployeeViewModel
Implements INotifyPropertyChanged
Public Sub New()
Dim employees = New ObservableCollection(Of Employee)()
Dim sharedEmployee = New Employee("Shared Employee")
employees.Add(New Manager("Manager 1") With { _
.Subordinates = New List(Of Employee)() From { _
New Employee("Employee 1"), _
New Employee("Employee 2"), _
New Employee("Employee 3"), _
New Employee("Employee 4"), _
sharedEmployee _
} _
})
employees.Add(New Manager("Manager 2") With { _
.Subordinates = New List(Of Employee)() From { _
sharedEmployee, _
New Employee("Employee 5"), _
New Employee("Employee 6"), _
New Employee("Employee 7"), _
New Employee("Employee 8"), _
New Employee("Employee 9") _
} _
})
employees.Add(New Employee("Employee 10"))
Employees = employees
End Sub
Private _employees As IList(Of Employee)
Public Property Employees() As IList(Of Employee)
Get
Return _employees
End Get
Set(value As IList(Of Employee))
_employees = value
OnPropertyChanged()
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(Optional propertyName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Set the ItemsSource property
Set DataContext to a new instance of the view model class and bind the ItemsSource to the Employees
property of the EmployeeViewModel
class.
In XAML:
<ig:XamDiagram x:Name="HierarchicalDiagram"
ItemsSource="{Binding Employees}">
<ig:XamDiagram.DataContext>
<local:EmployeeViewModel/>
</ig:XamDiagram.DataContext>
</ig:XamDiagram>
Usually for each of the data types in the ItemsSource a NodeDefinition is added to the xamDiagram . If one or more types are in an inheritance relationship the most concrete types have to be specified first. The xamDiagram tries to match the type of each of the data items the TargetType of a node definition. The first node definition whose TargetType returns true to a call to IsAssignableFrom
is selected. That is if the TargetType of a node definition matches exactly or is a parent type of the data item’s type, the node definition will be selected.
Add a NodeDefinition for the Manager class
Set the TargetType of the NodeDefinition to the Manager
type.
Set the ChildrenMemberPath to the name of the property holding the collection of child objects.
In this case that is “Subordinates” property.
Set the DisplayMemberPath
If a DisplayMemberPath is not specified, and if no custom DisplayTemplate is set via the NodeStyle, the result of the ToString
method will be displayed as the nodes’ content.
Set the NodeStyle (optional)
Using the NodeStyle property allows you to set a style to be applied to all DiagramNode objects matched by the node definition. This gives you the opportunity to easily customize the nodes created for a certain data type.
In XAML:
<ig:NodeDefinition
TargetType="{x:Type local:Manager}"
ChildrenMemberPath="Subordinates"
DisplayMemberPath="Name">
<ig:NodeDefinition.NodeStyle>
<Style TargetType="ig:DiagramNode">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Fill" Value="Green"/>
<Setter Property="ShapeType" Value="Ellipse"/>
<Setter Property="Stroke" Value="Transparent"/>
</Style>
</ig:NodeDefinition.NodeStyle>
</ig:NodeDefinition>
Add a node definition for the Employee class
Add an additional node definition setting the TargetType and DisplayMemberPath accordingly. Set the NodeStyle with a setter for the DisplayTemplate property as follows.
In XAML:
<ig:NodeDefinition
TargetType="{x:Type local:Employee}">
<ig:NodeDefinition.NodeStyle>
<Style TargetType="ig:DiagramNode">
<Setter Property="DisplayTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock
HorizontalAlignment="Center"
Text="{Binding Name}"
Foreground="Green"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Fill" Value="LightGreen"/>
<Setter Property="Stroke" Value="Transparent"/>
</Style>
</ig:NodeDefinition.NodeStyle>
</ig:NodeDefinition>
Create a ConnectionDefinition and add it to the ConnectionDefinitions collection. The connection definitions provide a way to set a custom style for the connection generated by the xamDiagram for parent-child data item relations. Connection definitions are matched by the StartTargetType and EndTargetType properties. For this example set these to the Manager
and Employee
types respectively.
Set the ConnectionStyle property to a style targeting DiagramConnection.
In XAML:
<ig:ConnectionDefinition
StartTargetType="{x:Type local:Manager}"
EndTargetType="{x:Type local:Employee}">
<ig:ConnectionDefinition.ConnectionStyle>
<Style TargetType="ig:DiagramConnection">
<Setter Property="Stroke" Value="DarkGreen"/>
<Setter Property="Foreground" Value="DarkGreen"/>
<Setter Property="Fill" Value="DarkGreen"/>
<Setter Property="Content" Value="Reports to"/>
<Setter Property="EndCapType" Value="None"/>
<Setter Property="StartCapType" Value="FilledArrow"/>
</Style>
</ig:ConnectionDefinition.ConnectionStyle>
</ig:ConnectionDefinition>
Following is the full code for this procedure.
In XAML:
<UserControl x:Class="DiagramDocumentationSamples.HierararchicalReferencesData"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ig="http://schemas.infragistics.com/xaml"
xmlns:local="clr-namespace:DiagramDocumentationSamples"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<ig:XamDiagram x:Name="HierarchicalDiagram" ItemsSource="{Binding Employees}">
<ig:XamDiagram.DataContext>
<local:EmployeeViewModel/>
</ig:XamDiagram.DataContext>
<ig:XamDiagram.NodeDefinitions>
<ig:NodeDefinition
TargetType="{x:Type local:Manager}"
ChildrenMemberPath="Subordinates"
DisplayMemberPath="Name">
<ig:NodeDefinition.NodeStyle>
<Style TargetType="ig:DiagramNode">
<Setter Property="FontSize" Value="18"/>
<Setter Property="Fill" Value="Green"/>
<Setter Property="ShapeType" Value="Ellipse"/>
<Setter Property="Stroke" Value="Transparent"/>
</Style>
</ig:NodeDefinition.NodeStyle>
</ig:NodeDefinition>
<ig:NodeDefinition
TargetType="{x:Type local:Employee}">
<ig:NodeDefinition.NodeStyle>
<Style TargetType="ig:DiagramNode">
<Setter Property="DisplayTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock
HorizontalAlignment="Center"
Text="{Binding Name}"
Foreground="Green"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Fill" Value="LightGreen"/>
<Setter Property="Stroke" Value="Transparent"/>
</Style>
</ig:NodeDefinition.NodeStyle>
</ig:NodeDefinition>
</ig:XamDiagram.NodeDefinitions>
<ig:XamDiagram.ConnectionDefinitions>
<ig:ConnectionDefinition
StartTargetType="{x:Type local:Manager}"
EndTargetType="{x:Type local:Employee}">
<ig:ConnectionDefinition.ConnectionStyle>
<Style TargetType="ig:DiagramConnection">
<Setter Property="Stroke" Value="DarkGreen"/>
<Setter Property="Foreground" Value="DarkGreen"/>
<Setter Property="Fill" Value="DarkGreen"/>
<Setter Property="Content" Value="Reports to"/>
<Setter Property="EndCapType" Value="None"/>
<Setter Property="StartCapType" Value="FilledArrow"/>
</Style>
</ig:ConnectionDefinition.ConnectionStyle>
</ig:ConnectionDefinition>
</ig:XamDiagram.ConnectionDefinitions>
<ig:XamDiagram.Layout>
<ig:ForceDirectedGraphDiagramLayout/>
</ig:XamDiagram.Layout>
</ig:XamDiagram>
</UserControl>
In C#:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
namespace DiagramDocumentationSamples
{
public partial class HierararchicalReferencesData : UserControl
{
public HierararchicalReferencesData()
{
InitializeComponent();
}
}
public class Employee : INotifyPropertyChanged
{
public Employee(string name)
{
Name = name;
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Manager : Employee
{
public Manager(string name) : base(name) { }
private IList<Employee> _subordinates;
public IList<Employee> Subordinates
{
get { return _subordinates; }
set
{
_subordinates = value;
OnPropertyChanged();
}
}
}
public class EmployeeViewModel : INotifyPropertyChanged
{
public EmployeeViewModel()
{
var employees = new ObservableCollection<Employee>();
var sharedEmployee = new Employee("Shared Employee");
employees.Add(new Manager("Manager 1")
{
Subordinates = new List<Employee>()
{
new Employee("Employee 1"),
new Employee("Employee 2"),
new Employee("Employee 3"),
new Employee("Employee 4"),
sharedEmployee
}
});
employees.Add(new Manager("Manager 2")
{
Subordinates = new List<Employee>()
{
sharedEmployee,
new Employee("Employee 5"),
new Employee("Employee 6"),
new Employee("Employee 7"),
new Employee("Employee 8"),
new Employee("Employee 9")
}
});
employees.Add(new Employee("Employee 10"));
Employees = employees;
}
private IList<Employee> _employees;
public IList<Employee> Employees
{
get { return _employees; }
set
{
_employees = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In VB:
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Imports System.Windows.Controls
Namespace DiagramDocumentationSamples
Public Partial Class HierararchicalReferencesData
Inherits UserControl
Public Sub New()
InitializeComponent()
End Sub
End Class
Public Class Employee
Implements INotifyPropertyChanged
Public Sub New(name As String)
Name = name
End Sub
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(value As String)
_name = value
OnPropertyChanged()
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(Optional propertyName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
Public Class Manager
Inherits Employee
Public Sub New(name As String)
MyBase.New(name)
End Sub
Private _subordinates As IList(Of Employee)
Public Property Subordinates() As IList(Of Employee)
Get
Return _subordinates
End Get
Set(value As IList(Of Employee))
_subordinates = value
OnPropertyChanged()
End Set
End Property
End Class
Public Class EmployeeViewModel
Implements INotifyPropertyChanged
Public Sub New()
Dim employees = New ObservableCollection(Of Employee)()
Dim sharedEmployee = New Employee("Shared Employee")
employees.Add(New Manager("Manager 1") With { _
.Subordinates = New List(Of Employee)() From { _
New Employee("Employee 1"), _
New Employee("Employee 2"), _
New Employee("Employee 3"), _
New Employee("Employee 4"), _
sharedEmployee _
} _
})
employees.Add(New Manager("Manager 2") With { _
.Subordinates = New List(Of Employee)() From { _
sharedEmployee, _
New Employee("Employee 5"), _
New Employee("Employee 6"), _
New Employee("Employee 7"), _
New Employee("Employee 8"), _
New Employee("Employee 9") _
} _
})
employees.Add(New Employee("Employee 10"))
Employees = employees
End Sub
Private _employees As IList(Of Employee)
Public Property Employees() As IList(Of Employee)
Get
Return _employees
End Get
Set(value As IList(Of Employee))
_employees = value
OnPropertyChanged()
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(Optional propertyName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
End Namespace
The following topics provide additional information related to this topic.