Back to Devexpress

Hierarchical Data Structure

wpf-402347-controls-and-libraries-navigation-controls-treeview-data-binding-options-hierarchical-data-structure.md

latest10.4 KB
Original Source

Hierarchical Data Structure

  • Sep 20, 2024
  • 7 minutes to read

A hierarchical data structure is a set of nested objects with a field that contains child records. Parents and children can be of different object types.

The DataControlBase.ItemsSource property contains only data items that correspond to root nodes. Use the following techniques to make the TreeViewControl work with the hierarchical data structure:

  • Child Nodes Path - Set a path to the children field. Use this technique only when child and parent fields have the same object type.
  • Child Nodes Selector - Create a selector that returns node children. You can use this technique for different object types.
  • Hierarchical Data Templates - Create a template for different data types.

Child Nodes Path

Use this technique to bind the TreeViewControl to a collection if all objects have the same field that contains child nodes.

To display a tree structure, set the TreeViewControl.ChildNodesPath property to a field that contains child nodes (the Employees field in the code sample below).

View Example

xaml
<dxg:TreeViewControl ItemsSource="{Binding EmployeeDepartments}" 
                     ChildNodesPath="Employees" 
                     TreeViewFieldName="Name"/>
csharp
using System.Windows;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using DevExpress.Mvvm;

namespace TreeViewChildNodesSelector {
    public class MainWindowViewModel : ViewModelBase {
        public MainWindowViewModel() {
            EmployeeDepartments = Departments.GetDepartments();
        }
        public List<EmployeeDepartment> EmployeeDepartments { get; set; }
    }
    public class Employee {
        public Employee(int id, string name) {
            ID = id;
            Name = name;
        }
        public int ID { get; set; }
        public string Name { get; set; }
    }
    public class EmployeeDepartment {
        public string Name { get; set; }
        public ObservableCollection<Employee> Employees { get; }

        public EmployeeDepartment(string name, IEnumerable<Employee> employees) {
            Name = name;
            Employees = new ObservableCollection<Employee>(employees);
        }
    }
    public static class Departments {
        public static List<EmployeeDepartment> GetDepartments() {
            List<EmployeeDepartment> departments = new List<EmployeeDepartment> {
                new EmployeeDepartment("Management", new Employee[] {
                new Employee(0, "Gregory S. Price")
            }, true),
            new EmployeeDepartment("Marketing", new Employee[] {
                new Employee(1, "Irma R. Marshall"),
                new Employee(2, "Brian C. Cowling"),
                new Employee(3, "Thomas C. Dawson"),
                new Employee(4, "Bryan R. Henderson"),
            }, true),
            new EmployeeDepartment("Operations", new Employee[] {
                new Employee(5, "John C. Powell"),
                new Employee(6, "Harold S. Brandes"),
                new Employee(7, "Jan K. Sisk"),
                new Employee(8, "Sidney L. Holder"),
            }, true),
            new EmployeeDepartment("Production", new Employee[] {
                new Employee(9, "Christian P. Laclair"),
                new Employee(10, "James L. Kelsey"),
                new Employee(11, "Howard M. Carpenter"),
                new Employee(12, "Jennifer T. Tapia"),
            },false),
            new EmployeeDepartment("Finance", new Employee[] {
                new Employee(13, "Karen J. Kelly"),
                new Employee(14, "Judith P. Underhill"),
                new Employee(15, "Russell E. Belton"),
            },false) };
            return departments;
        }
    }
}
vb
Imports System.Windows
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports DevExpress.Mvvm

Namespace TreeViewChildNodesSelector
    Public Class MainWindowViewModel
        Inherits ViewModelBase

        Public Sub New()
            EmployeeDepartments = Departments.GetDepartments()
        End Sub

        Public Property EmployeeDepartments As List(Of EmployeeDepartment)
    End Class

    Public Class Employee
        Public Sub New(ByVal id As Integer, ByVal name As String)
            ID = id
            Name = name
        End Sub

        Public Property ID As Integer
        Public Property Name As String
    End Class

    Public Class EmployeeDepartment
        Public Property Name As String
        Public ReadOnly Property Employees As ObservableCollection(Of Employee)
        Public Property HasChildNodes As Boolean

        Public Sub New(ByVal name As String, ByVal employees As IEnumerable(Of Employee))
            Name = name
            Employees = New ObservableCollection(Of Employee)(employees)
        End Sub
    End Class

    Module Departments
        Function GetDepartments() As List(Of EmployeeDepartment)
            Dim departments As List(Of EmployeeDepartment) = New List(Of EmployeeDepartment) From {
                New EmployeeDepartment("Management", New Employee() {New Employee(0, "Gregory S. Price")}, True),
                New EmployeeDepartment("Marketing", New Employee() {New Employee(1, "Irma R. Marshall"), New Employee(2, "Brian C. Cowling"), New Employee(3, "Thomas C. Dawson"), New Employee(4, "Bryan R. Henderson")}, True),
                New EmployeeDepartment("Operations", New Employee() {New Employee(5, "John C. Powell"), New Employee(6, "Harold S. Brandes"), New Employee(7, "Jan K. Sisk"), New Employee(8, "Sidney L. Holder")}, True),
                New EmployeeDepartment("Production", New Employee() {New Employee(9, "Christian P. Laclair"), New Employee(10, "James L. Kelsey"), New Employee(11, "Howard M. Carpenter"), New Employee(12, "Jennifer T. Tapia")}, False),
                New EmployeeDepartment("Finance", New Employee() {New Employee(13, "Karen J. Kelly"), New Employee(14, "Judith P. Underhill"), New Employee(15, "Russell E. Belton")}, False)
            }
            Return departments
        End Function
    End Module
End Namespace

Child Nodes Selector

Use this technique to display a hierarchical data structure for different object types.

If all objects have the same field that contains child nodes, use the Child Nodes Path technique.

An example of this structure is shown below:

csharp
public class ProjectObject : BaseObject {
    public ObservableCollection<ProjectStage> Stages { get; set; }
}

public class ProjectStage : BaseObject {
    public ObservableCollection<Task> Tasks { get; set; }
}

public class Task : BaseObject {
    State state;
    // ...
}
vb
Public Class ProjectObject
    Inherits BaseObject

    Public Property Stages As ObservableCollection(Of ProjectStage)
End Class

Public Class ProjectStage
    Inherits BaseObject

    Public Property Tasks As ObservableCollection(Of Task)
End Class

Public Class Task
    Inherits BaseObject

    Private state As State
    ' ...
End Class
  1. Create a selector class that implements IChildNodesSelector, and override the SelectChildren(Object) method that returns node children.

  2. Assign the Child Nodes Selector to the ChildNodesSelector property.

Hierarchical Data Templates

You can use templates to display a hierarchical data structure in the TreeViewControl.

This technique is slower than the Child Nodes Path and Child Nodes Selector because it uses the standard Binding mechanism to obtain child nodes.

An example of this structure is shown below:

csharp
public class ProjectObject : BaseObject {
    public ObservableCollection<ProjectStage> Stages { get; set; }
}

public class ProjectStage : BaseObject {
    public ObservableCollection<Task> Tasks { get; set; }
}

public class Task : BaseObject {
    State state;
    // ...
}
vb
Public Class ProjectObject
    Inherits BaseObject

    Public Property Stages As ObservableCollection(Of ProjectStage)
End Class

Public Class ProjectStage
    Inherits BaseObject

    Public Property Tasks As ObservableCollection(Of Task)
End Class

Public Class Task
    Inherits BaseObject

    Private state As State
    ' ...
End Class

If all objects have the same field that contains child nodes, create a hierarchical data template and assign it to the NodeTemplate property. You can put hierarchical data templates into resources. Specify the data type to which a template should be applied:

xaml
<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:ProjectObject}" ItemsSource="{Binding Path=Stages}" />
    <HierarchicalDataTemplate DataType="{x:Type local:ProjectStage}" ItemsSource="{Binding Path=Tasks}" />
</Window.Resources>

<dxg:TreeViewControl ItemsSource="{Binding DataItems}" 
                     TreeViewFieldName="Name" 
                     TreeDerivationMode="HierarchicalDataTemplate"/>

If all objects have different fields that contain child nodes:

  1. Create a template selector that implements the System.Windows.Controls.DataTemplateSelector and overrides the SelectTemplate method.

  2. Assign the template selector to the NodeTemplateSelector property.

  3. Set the TreeDerivationMode property to HierarchicalDataTemplate.