Binding to Hierarchical Data Structure
- 5 minutes to read
When working with hierarchical data structures, the control’s DataControlBase.ItemsSource property contains only data items that correspond to root nodes.
- ChildNodesPath - use this approach with the same object type for child and parent fields only. Use the TreeListView.ChildNodesPath property to select the required field.
- ChildNodesSelector - create a selector that returns the list of child nodes for the processed node. You can use this approach for different object types.
- Hierarchical Data Templates - create a template for different data types.
Using ChildNodesPath
Use the TreeListView.ChildNodesPath to bind the TreeListControl to a collection if all business objects have the same child field. An example of such a structure is shown below.
public class BaseObject {
public string Name { get; set; }
public String Executor { get; set; }
public ObservableCollection<Task> Tasks { get; set; }
public override string ToString() { return Name; }
}
public class ProjectObject : BaseObject {}
public class Task : BaseObject {
public string State { get; set; }
}
To bind a collection to the TreeList View, assign the child field name to the TreeListView’s TreeListView.ChildNodesPath property and set the TreeListView.TreeDerivationMode property to TreeDerivationMode.ChildNodesSelector. In the example below the Task field is the child field.
<dxg:TreeListControl Name="treeListView1" ItemsSource="{Binding DataItems}">
<dxg:TreeListControl.Columns>
<dxg:TreeListColumn FieldName="Name" AllowSorting="True" />
<dxg:TreeListColumn FieldName="Executor" AllowSorting="True" />
<dxg:TreeListColumn FieldName="State" AllowSorting="True" />
</dxg:TreeListControl.Columns>
<dxg:TreeListControl.View>
<dxg:TreeListView TreeDerivationMode="ChildNodesSelector" ChildNodesPath="Tasks"/>
</dxg:TreeListControl.View>
</dxg:TreeListControl>
Example: How to: Implement ChildNodesPath
Building a Tree in ‘Code Behind’
This approach allows you to create a hierarchical data structure in the TreeView using the TreeListView.ChildNodesSelector property. An example of this structure is shown below:
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;
// ...
}
You should specify the origin of a data object’s child item programmatically when using this approach. Create a selector class that implements DevExpress.Xpf.Grid.IChildNodeSelector, and override the SelectChildren method to return the specified node’s list of child nodes.
For the Project-Stage-Task business class structure, the selector class is as follows:
public class CustomChildrenSelector : IChildNodesSelector {
public IEnumerable SelectChildren(object item) {
if (item is ProjectStage)
return ((ProjectStage)item).Tasks;
else if (item is ProjectObject)
return ((ProjectObject)item).Stages;
return null;
}
}
Finally, you should assign the Child Nodes Selector to the TreeListView’s TreeListView.ChildNodesSelector property and set the TreeListView.TreeDerivationMode property to ‘ChildNodesSelector’.
<dxg:TreeListControl x:Name="treeList"
ItemsSource="{Binding DataItems}"
DataContext="{StaticResource viewModel}">
<dxg:TreeListControl.Resources>
<local:CustomChildrenSelector x:Key="childrenSelector"/>
</dxg:TreeListControl.Resources>
<dxg:TreeListControl.Columns>
<dxg:TreeListColumn FieldName="Name" />
<dxg:TreeListColumn FieldName="Executor" />
<dxg:TreeListColumn FieldName="State" />
</dxg:TreeListControl.Columns>
<dxg:TreeListControl.View>
<dxg:TreeListView x:Name="view" TreeDerivationMode="ChildNodesSelector"
ChildNodesSelector="{StaticResource childrenSelector}"/>
</dxg:TreeListControl.View>
</dxg:TreeListControl>
Note
If all business objects have the same ‘children’ field, assign its name to the TreeListView’s TreeListView.ChildNodesPath property. Otherwise, create a Child Nodes Selector.
Example: How to: Implement Hierarchical Data Binding via Child Nodes Selector
Using Hierarchical Data Templates
Hierarchical Data Templates allows you to create a hierarchical data structure in the TreeView using templates. An example of this structure is shown below:
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;
// ...
}
If all business objects have the same children field/property, create a single template and assign it to the DataRowTemplate property.
Hierarchical data templates can work without a key in your resources. Instead of key, specify the data type it should be applied to:
<ResourceDictionary>
<HierarchicalDataTemplate DataType="{x:Type local:ProjectObject}" ItemsSource="{Binding Path=Stages}" />
<HierarchicalDataTemplate DataType="{x:Type local:ProjectStage}" ItemsSource="{Binding Path=Tasks}" />
</ResourceDictionary>
<dxg:TreeListControl.View>
<dxg:TreeListView TreeDerivationMode="HierarchicalDataTemplate" />
</dxg:TreeListControl.View>
Note
First, the TreeListView verifies its DataRowTemplateSelector property. If the specified node selector returns null, or the DataRowTemplateSelector property is not specified, it uses a template the DataRowTemplate property specifies If this property is not specified, an implicit hierarchical data template is used.
Once the required HierarchicalDataTemplate is defined for a node, its ItemTemplate and ItemTemplateSelector properties take priority over the corresponding properties the TreeListView (DataRowTemplate and DataRowTemplateSelector) specifies
Use the DataRowTemplateSelector approach if all business objects have different children field/properties:
<Window.Resources>
<ResourceDictionary>
<local:CustomHierarchicalDataTemplateSelector x:Key="selector">
<local:CustomHierarchicalDataTemplateSelector.ProjectDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Stages}" />
</local:CustomHierarchicalDataTemplateSelector.ProjectDataTemplate>
<local:CustomHierarchicalDataTemplateSelector.ProjectStageDataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Tasks}" />
</local:CustomHierarchicalDataTemplateSelector.ProjectStageDataTemplate>
</local:CustomHierarchicalDataTemplateSelector>
</ResourceDictionary>
</Window.Resources>
Create a template selector that implements the System.Windows.Controls.DataTemplateSelector and overrides the SelectTemplate method.
public class CustomHierarchicalDataTemplateSelector : DataTemplateSelector {
public HierarchicalDataTemplate ProjectDataTemplate { get; set; }
public HierarchicalDataTemplate ProjectStageDataTemplate { get; set; }
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) {
var rowData = item as TreeListRowData;
var element = rowData.Row;
if (element is ProjectObject)
return ProjectDataTemplate;
if (element is ProjectStage)
return ProjectStageDataTemplate;
return null;
}
}
Assign the template selector to the TreeListView’s DataRowTemplateSelector property and switch to the HierarchicalDataTemplate tree derivation mode using the TreeListView.TreeDerivationMode property.
<dxg:TreeListControl.View>
<dxg:TreeListView DataRowTemplateSelector="{StaticResource selector}"
TreeDerivationMode="HierarchicalDataTemplate" />
</dxg:TreeListControl.View>
Example: How to: Build a Tree via HierarchicalDataTemplate
Limitations
When a Hierarchical binding mode is used, it is possible to sort and filter by a field value(s) when child’s object types are the same. Set the TreeListView.AutoDetectColumnTypeInHierarchicalMode property to True to enable sorting (enabled by default).