Version

Binding to Hierarchical Node Data with References (xamDiagram)

Topic Overview

Purpose

This topic provides a step by step guidance on how to bind the xamDiagram™ control to hierarchical node data.

Required background

The following topics are prerequisites to understanding this topic:

Topic Purpose

This topic provides an overview of the data binding scenarios supported by the xamDiagram control.

Binding xamDiagram to Hierarchical Node Data with References

Introduction

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.

Preview

The following screenshot is a preview of the result.

xamDiagram Bind To Hierarchical Node Data With References 1.png

Prerequisites

To complete the procedure, you need the following:

Overview

Following is a conceptual overview of the process:

  1. Configure the ItemsSource property

  2. Create the node definitions

  3. Create the connection definitions

Steps

The following steps demonstrate how to bind the xamDiagram to hierarchical node data with references.

1. Configure the ItemsSource property

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>

2. Create the node definitions

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

  1. Set the TargetType of the NodeDefinition to the Manager type.

  2. Set the ChildrenMemberPath to the name of the property holding the collection of child objects.
    In this case that is “Subordinates” property.

  3. 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.

  4. 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>

3. Create a hierarchical connection definition.

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>

Full code

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

Related Topics

The following topics provide additional information related to this topic.

Topic Purpose

This topic provides a step by step guidance on how to bind the xamDiagram control to data where input data objects represent nodes and connection and each of the connection objects has a reference to two node objects representing its start and end nodes.

This topic provides a step by step guidance on how to bind the xamDiagram control to data where input data objects represent nodes and connections. Each of the node objects has a unique string identifier and each of the connection objects has two properties holding the identifiers of the two node objects representing its start and end nodes.

This topic provides a step by step guidance on how to bind the xamDiagram control to hierarchical node data with keys.