Table of Contents

Binding to Hierarchical Data

In a typical hierarchical data source, a business object has a property that stores a collection of child data objects. Two approaches allow you to supply child data to the TreeList and TreeView controls in hierarchical binding mode:

Parent and child data objects can be of different types, but they should share a set of identical properties that the TreeList control displays as columns.

Dynamic Data Load

When bound to a hierarchical data source, TreeList and TreeView controls load nodes on demand by default: child nodes are dynamically loaded when a parent node is expanded.

Set the AllowDynamicDataLoading property to false to load all nodes simultaneously after you bind the control to the data source.

If you use dynamic node loading, the TreeList and TreeView controls do not have access to nodes (and their underlying data) that haven't been loaded. This applies the following restrictions to the node checking and filter/search functionalities:

  • When you check a parent node in recursive mode (see AllowRecursiveNodeChecking), the control checks this node along with currently loaded child nodes. The control does not check child nodes that haven't been loaded.

  • When you search for data in the built-in Search box, or filter data with the auto-filter row, the control searches for data only across currently loaded nodes.

Specify Path to Child Data

One approach to supply child data to the TreeList/TreeView control is to specify the name of the property that stores child data in a business object. Use the ChildrenFieldName property for this purpose. For instance, if a business object stores child data in the Items collection, set the ChildrenFieldName property to "Items".

You also need to set the HasChildrenFieldName property to the name of the property that returns true if a business object has child data, and false, otherwise. This information is required to display or hide node expand buttons. If you do not specify the HasChildrenFieldName property the control displays expand buttons for childless nodes until a user tries to expand these nodes.

The following example demonstrates how to bind a TreeList control to data. An Employee object in the example has the Subordinates collection that contains child items. The ChildrenFieldName property specifies the name of the property (the "Subordinates" string) that stores child data.

xmlns:mxtl="https://schemas.eremexcontrols.net/avalonia/treelist"
...
<mxtl:TreeListControl Grid.Column="0" Width="400" Name="treeList1" >
    <mxtl:TreeListControl.Columns>
        <mxtl:TreeListColumn Name="colName" FieldName="Name" Header="Name" />
        <mxtl:TreeListColumn Name="colBirthdate" FieldName="Birthdate" Header="Birthdate"/>
    </mxtl:TreeListControl.Columns>
</mxtl:TreeListControl>
using CommunityToolkit.Mvvm.ComponentModel;

Employee p1 = new Employee() { Name = "Mark Douglas", Birthdate=new DateTime(1990, 01, 5) };
Employee p2 = new Employee() { Name = "Mary Watson", Birthdate = new DateTime(1985, 12, 17) };
Employee p3 = new Employee() { Name = "Alex Wude", Birthdate = new DateTime(2000, 10, 7) };
Employee p4 = new Employee() { Name = "Sam Louis", Birthdate = new DateTime(1975, 8, 27) };
Employee p5 = new Employee() { Name = "Dan Miller", Birthdate = new DateTime(1981, 3, 6) };
p1.Subordinates.Add(p2);
p1.Subordinates.Add(p3);
p2.Subordinates.Add(p4);
p3.Subordinates.Add(p5);

ObservableCollection<Employee> employees = new ObservableCollection<Employee>() { p1 };

treeList1.ChildrenFieldName = "Subordinates";
treeList1.HasChildrenFieldName = "HasChildren";

treeList1.ItemsSource = employees;

public partial class Employee : ObservableObject
{
    [ObservableProperty]
    public string name="";

    [ObservableProperty]
    public DateTime? birthdate=null;

    public ObservableCollection<Employee> Subordinates { get; } = new();

    public bool HasChildren { get { return Subordinates.Count > 0; } }
}

Specify Child Data Selector

Another approach to supply child data in hierarchical binding mode is to implement a selector. The selector specifies whether child data is available in a business object, and returns this child data on demand. The TreeList/TreeView control uses the information supplied by the selector to create child nodes and draw node expand buttons.

Use the ChildrenSelector property to assign a selector to your control. A selector is an object that implements the ITreeListChildrenSelector interface:

public interface ITreeListChildrenSelector
{
    // The method should return whether a business object has child data. 
    // The control uses this information to display or hide node expand buttons.
    bool HasChildren(object item) => true;
    // The method should return child objects for a business object.
    IEnumerable? SelectChildren(object item);
}

The following example creates a selector (MyTreeListChildrenSelector) that supplies information on child data for a TreeList control.

xmlns:mxtl="https://schemas.eremexcontrols.net/avalonia/treelist"
...
<Grid RowDefinitions="Auto, Auto, Auto, Auto" ColumnDefinitions="400, *">
    <Grid.Resources>
        <local:MyTreeListChildrenSelector x:Key="mySelector" />
    </Grid.Resources>

    <mxtl:TreeListControl Grid.Column="0" Name="treeList2"
     ChildrenSelector="{StaticResource mySelector}">
        <mxtl:TreeListControl.Columns>
            <mxtl:TreeListColumn Name="colName1" FieldName="Name" />
            <mxtl:TreeListColumn Name="colBirthdate1" FieldName="Birthdate"/>
        </mxtl:TreeListControl.Columns>
    </mxtl:TreeListControl>
</Grid>
using CommunityToolkit.Mvvm.ComponentModel;

Employee p1 = new Employee() { Name = "Mark Douglas", Birthdate=new DateTime(1990, 01, 5)};
Employee p2 = new Employee() { Name = "Mary Watson", Birthdate = new DateTime(1985, 12, 17)};
Employee p3 = new Employee() { Name = "Alex Wude", Birthdate = new DateTime(2000, 10, 7)};
Employee p4 = new Employee() { Name = "Sam Louis", Birthdate = new DateTime(1975, 8, 27)};
Employee p5 = new Employee() { Name = "Dan Miller", Birthdate = new DateTime(1981, 3, 6)};
p1.Subordinates.Add(p2);
p1.Subordinates.Add(p3);
p2.Subordinates.Add(p4);
p3.Subordinates.Add(p5);

ObservableCollection<Employee> employees = new ObservableCollection<Employee>() { p1 };

treeList2.ItemsSource = employees;

public class MyTreeListChildrenSelector : ITreeListChildrenSelector
{
    public bool HasChildren(object? item) => (item as Employee).HasChildren;
    public IEnumerable? SelectChildren(object? item) => (item as Employee).Subordinates;
}

public partial class Employee : ObservableObject
{
    [ObservableProperty]
    public string name="";

    [ObservableProperty]
    public DateTime? birthdate=null;

    public ObservableCollection<Employee> Subordinates = new();

    public bool HasChildren { get { return Subordinates.Count > 0; } }
}

See Also