windowsforms-1472-controls-and-libraries-tree-list-feature-center-nodes-tree-traversal.md
The TreeList control arranges data records in a tree structure — a node can have child nodes. To traverse the tree you can use either a recursive method or an iterator.
You can enumerate nodes in a tree with a recursive method — a method that calls itself in its body. Typically, you initially call a recursive method on a node from which you start to enumerate nodes, and then, you recursively call it on a child or parent node depending on the direction. To get access to the tree structure, use the following properties:
In the example below, only leaf nodes (nodes that have no children) have values in the Budget column. The code below shows how to calculate parent node values when the control is loaded. The code also updates parent nodes when a cell value changes.
The code below also shows how to update child nodes when a cell value in the Location column changes.
using System;
using DevExpress.XtraTreeList;
using DevExpress.XtraTreeList.Nodes;
private void treeList1_Load(object sender, EventArgs e) {
UpdateNodes(treeList1.Nodes, "BUDGET");
}
int lockChanged = 0;
int UpdateNodes(TreeListNodes nodes, string fieldName) {
lockChanged++;
int sum = 0;
foreach (TreeListNode treeListNode in nodes) {
if (treeListNode.HasChildren)
treeListNode.SetValue(fieldName, UpdateNodes(treeListNode.Nodes, fieldName));
sum += Convert.ToInt32(treeListNode.GetValue(fieldName));
}
lockChanged--;
return sum;
}
private void treeList1_CellValueChanged(object sender, CellValueChangedEventArgs e) {
if (lockChanged == 0) {
if(e.Column.FieldName == "BUDGET")
UpdateParentNodes(e.Node, e.Column.FieldName);
if (e.Column.FieldName == "LOCATION")
UpdateChildNodes(e.Node, e.Column.FieldName);
}
}
void UpdateParentNodes(TreeListNode node, string fieldName) {
TreeListNode parent = node.ParentNode;
if (parent == null || String.IsNullOrEmpty(fieldName))
return;
lockChanged++;
int sum = 0;
foreach (TreeListNode childNode in parent.Nodes) {
sum += Convert.ToInt32(childNode.GetValue(fieldName));
}
// The following SetValue method call does not raise the CellValueChanged event,
// since it is called in a CellValueChanged event handler.
parent.SetValue(fieldName, sum);
// Call the method recursively to update the current node's parent nodes.
UpdateParentNodes(parent, fieldName);
lockChanged--;
}
void UpdateChildNodes(TreeListNode node, string fieldName) {
if (!node.HasChildren || String.IsNullOrEmpty(fieldName))
return;
lockChanged++;
foreach(TreeListNode childNode in node.Nodes) {
childNode.SetValue(fieldName, Convert.ToString(node.GetValue(fieldName)));
UpdateChildNodes(childNode, fieldName);
}
lockChanged--;
}
Imports System
Imports DevExpress.XtraTreeList
Imports DevExpress.XtraTreeList.Nodes
Private Sub treeList1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles treeList1.Load
UpdateNodes(treeList1.Nodes, "BUDGET")
End Sub
Private lockChanged As Integer = 0
Private Function UpdateNodes(ByVal nodes As TreeListNodes, ByVal fieldName As String) As Integer
lockChanged += 1
Dim sum As Integer = 0
For Each treeListNode As TreeListNode In nodes
If treeListNode.HasChildren Then
treeListNode.SetValue(fieldName, UpdateNodes(treeListNode.Nodes, fieldName))
End If
sum += Convert.ToInt32(treeListNode.GetValue(fieldName))
Next treeListNode
lockChanged -= 1
Return sum
End Function
Private Sub treeList1_CellValueChanged(ByVal sender As Object, ByVal e As CellValueChangedEventArgs) Handles treeList1.CellValueChanged
If lockChanged = 0 Then
If e.Column.FieldName = "BUDGET" Then
UpdateParentNodes(e.Node, e.Column.FieldName)
End If
If e.Column.FieldName = "LOCATION" Then
UpdateChildNodes(e.Node, e.Column.FieldName)
End If
End If
End Sub
Private Sub UpdateParentNodes(ByVal node As TreeListNode, ByVal fieldName As String)
Dim parentNode As TreeListNode = node.ParentNode
If parentNode Is Nothing OrElse String.IsNullOrEmpty(fieldName) Then
Return
End If
lockChanged += 1
Dim sum As Integer = 0
For Each childNode As TreeListNode In parentNode.Nodes
sum += Convert.ToInt32(childNode.GetValue(fieldName))
Next childNode
' The following SetValue method call does not raise the CellValueChanged event,
' since it is called in a CellValueChanged event handler.
parentNode.SetValue(fieldName, sum)
' Call the method recursively to update the current node's parent nodes.
UpdateParentNodes(parentNode, fieldName)
lockChanged -= 1
End Sub
Private Sub UpdateChildNodes(ByVal node As TreeListNode, ByVal fieldName As String)
If Not node.HasChildren OrElse String.IsNullOrEmpty(fieldName) Then
Return
End If
lockChanged += 1
For Each childNode As TreeListNode In node.Nodes
childNode.SetValue(fieldName, Convert.ToString(node.GetValue(fieldName)))
UpdateChildNodes(childNode, fieldName)
Next childNode
lockChanged -= 1
End Sub
The TreeList.NodesIterator property provides access to a TreeListNodesIterator object that allows you to enumerate nodes without the use of a recursive method.
Follow the steps below to enumerate all nodes and execute a custom operation on each node.
Specify the operation executed on nodes.
Enumerate nodes and execute the specified operation on each row.
The following example shows how to use the Nodes Iterator to obtain the number of nodes which reside on the specified nesting level.
In the example, a CustomNodeOperation object is created that is used to calculate this number. The operation class contains an internal counter that is incremented by one each time a node that resides at the specified nesting level is accessed.
To get the total number of the nodes which reside at the specified nesting level, a CustomNodeOperation instance is created and passed to the TreeListNodesIterator.DoLocalOperation method. After the method has been performed, the CustomNodeOperation.NodeCount property is read to get the number of nodes.
using DevExpress.XtraTreeList.Nodes;
using DevExpress.XtraTreeList.Nodes.Operations;
// Declaring the custom operation class.
class CustomNodeOperation : TreeListOperation {
int level;
int nodeCount;
public CustomNodeOperation(int level) : base() {
this.level = level;
this.nodeCount = 0;
}
public override void Execute(TreeListNode node) {
if(node.Level == level)
nodeCount++;
}
public int NodeCount {
get { return nodeCount; }
}
}
// ...
CustomNodeOperation operation = new CustomNodeOperation(2);
treeList1.NodesIterator.DoLocalOperation(operation, treeList1.Nodes);
int totalNodesAtSecondLevel = operation.NodeCount;
Imports DevExpress.XtraTreeList.Nodes
Imports DevExpress.XtraTreeList.Nodes.Operations
' Declaring the custom operation class.
Class CustomNodeOperation
Inherits TreeListOperation
Private level As Integer
Private nodeCountCore As Integer
Public Sub New(ByVal level As Integer)
Me.level = level
Me.nodeCountCore = 0
End Sub 'New
Public Overrides Sub Execute(ByVal node As TreeListNode)
If node.Level = level Then
nodeCountCore += 1
End If
End Sub 'Execute
Public ReadOnly Property NodeCount() As Integer
Get
Return nodeCountCore
End Get
End Property
End Class
'...
Dim operation As CustomNodeOperation = New CustomNodeOperation(2)
TreeList1.NodesIterator.DoLocalOperation(operation, TreeList1.Nodes)
Dim totalNodesAtSecondLevel As Integer = operation.NodeCount
You can override an operation’s following properties and methods to enumerate specific nodes only:
The CanExecute(TreeListNode) method — returns whether the operation can be executed on the processed node. Returns true if not overridden.
The CanContinueIteration(TreeListNode) method — called before and after the operation is executed on the processed node and returns a value that specifies whether to continue to enumerate nodes. Returns true if not overridden.
The NeedsVisitChildren(TreeListNode) method — returns a value that specifies whether to enumerate the processed node’s child nodes. Returns true if not overridden.
The NeedsFullIteration property — gets or sets whether to enumerate leaf nodes (nodes without children). Returns true if not overridden.
The TreeListOperation.FinalizeOperation method — called when the process is finished and allows you to release allocated resources.
The following example shows how to create an operation class that will collapse all the nodes which do not contain the specified node as their child. Only the node’s parents will be expanded as a result.
The operation class accepts the node as its constructor parameter and stores it in an internal variable. The Execute method checks whether the processed node contains the specified node as a child. If not, the processed node is collapsed. The operation class also overrides the TreeListOperation.NeedsFullIteration property to process only the nodes that have children. This provides performance benefits when working with large and complex node structures.
The image below shows the Tree List before and after executing the sample code:
using DevExpress.XtraTreeList.Nodes;
using DevExpress.XtraTreeList.Nodes.Operations;
public class CollapseExceptSpecifiedOperation : TreeListOperation {
TreeListNode visibleNode;
public CollapseExceptSpecifiedOperation(TreeListNode visibleNode) : base() {
this.visibleNode = visibleNode;
}
public override void Execute(TreeListNode node) {
if (!visibleNode.HasAsParent(node))
node.Expanded = false;
}
public override bool NeedsFullIteration { get { return false; } }
}
//...
treeList1.NodesIterator.DoOperation(new CollapseExceptSpecifiedOperation(treeList1.FocusedNode));
Imports DevExpress.XtraTreeList.Nodes
Imports DevExpress.XtraTreeList.Nodes.Operations
Public Class CollapseExceptSpecifiedOperation
Inherits TreeListOperation
Private visibleNode As TreeListNode
Public Sub New(ByVal visibleNode As TreeListNode)
Me.visibleNode = visibleNode
End Sub 'New
Public Overrides Sub Execute(ByVal node As TreeListNode)
If Not visibleNode.HasAsParent(node) Then
node.Expanded = False
End If
End Sub 'Execute
Public Overrides ReadOnly Property NeedsFullIteration() As Boolean
Get
Return False
End Get
End Property
End Class
'...
TreeList1.NodesIterator.DoOperation(New CollapseExceptSpecifiedOperation(TreeList1.FocusedNode))