Tree Traversal 树遍历

Goto Tree List 树列表

Tree Traversal 树遍历

TreeList 控件以树结构排列数据记录 — 一个节点可以有子节点。要遍历树,可以使用递归方法或迭代器。

Recursive Method 递归方法

您可以使用递归方法枚举树中的节点 — 一种在其主体中调用自身的方法。通常,您最初在开始枚举节点的节点上调用递归方法,然后根据方向在子节点或父节点上递归调用该方法。要访问树结构,请使用以下属性:

  • TreeList.Nodes — 提供对根节点的访问。
  • TreeListNode.Nodes — 提供对节点的子节点的访问。您还可以使用 TreeListNode.HasChildren 属性来检查节点是否具有子节点。
  • TreeListNode.ParentNode — 提供对节点的父节点的访问。

在下面的示例中,只有叶节点(没有子节点的节点)在 Budget 列中具有值。下面的代码显示了如何在加载控件时计算父节点值。当单元格值发生更改时,该代码还会更新父节点。

下面的代码还显示了当 Location 列中的单元格值发生更改时如何更新子节点。

在这里插入图片描述

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--;
}

Node Iterator 节点迭代器

TreeList.NodesIterator 属性提供对 TreeListNodesIterator 对象的访问,该对象允许您在不使用递归方法的情况下枚举节点。

Enumerate All Nodes 枚举所有节点

按照以下步骤枚举所有节点并在每个节点上执行自定义操作。

  • 指定在节点上执行的操作。
    • 继承自 TreeListOperation 抽象类。
    • 重写 TreeListOperation.Execute 方法。每次枚举节点时都会调用此方法,并接受该节点作为参数。
public class MyOperation : TreeListOperation {
    public override void Execute(TreeListNode node) {
        //Do something.
    }
}

您还可以指定应枚举的节点。请参阅下面的枚举特定节点。

  • 枚举节点并对每一行执行指定的操作。
    • 使用 TreeList.NodesIterator 属性获取迭代器。
    • 将创建的操作传递给 TreeListNodesIterator.DoOperation 或 TreeListNodesIterator.DoLocalOperation 方法。
treeList1.NodesIterator.DoOperation(new MyOperation());

您还可以使用 lambda 表达式。下面的代码显示了如何对节点进行计数。

int count = 0;
treeList1.NodesIterator.DoOperation((n) => count++);

如何:对特定级别的节点进行计数

以下示例显示如何使用 Nodes Iterator 获取驻留在指定嵌套级别的节点数。

在该示例中,创建了一个 CustomNodeOperation 对象,用于计算此数字。操作类包含一个内部计数器,每次访问位于指定嵌套级别的节点时,该计数器都会递增 1。

要获取驻留在指定嵌套级别的节点总数,创建一个 CustomNodeOperation 实例并将其传递给 TreeListNodesIterator.DoLocalOperation 方法。执行该方法后,将读取 CustomNodeOperation.NodeCount 属性以获取节点数。

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;

Enumerate Specific Nodes 枚举特定节点

您可以覆盖操作的以下属性和方法,以仅枚举特定节点:

  • CanExecute(TreeListNode) 方法 — 返回是否可以在已处理的节点上执行操作。如果未覆盖,则返回 true。

  • CanContinueIteration(TreeListNode) 方法 — 在已处理的节点上执行操作之前和之后调用,并返回一个值,该值指定是否继续枚举节点。如果未覆盖,则返回 true。
    使用此方法可提高性能。例如,如果您需要查找节点。

  • NeedsVisitChildren(TreeListNode) 方法 — 返回一个值,该值指定是否枚举已处理节点的子节点。如果未覆盖,则返回 true。
    使用此方法可提高性能。例如,如果只需要枚举根节点。

  • NeedsFullIteration 属性 — 获取或设置是否枚举叶节点(没有子节点的节点)。如果未覆盖,则返回 true。
    使用此属性可提高性能。例如,如果该操作展开了折叠的节点,则无需枚举叶节点。

  • TreeListOperation.FinalizeOperation 方法 — 在流程完成时调用,并允许您释放分配的资源。

如何:折叠特定节点

以下示例演示如何创建一个操作类,该类将折叠所有不包含指定节点作为其子节点的节点。因此,只有节点的父节点才会被扩展。

操作类接受节点作为其构造函数参数,并将其存储在内部变量中。Execute 方法检查处理的节点是否包含指定的节点作为子节点。否则,已处理的节点将折叠。操作类还重写 TreeListOperation.NeedsFullIteration 属性,以仅处理具有子节点的节点。这在处理大型复杂节点结构时提供了性能优势。

下图为执行示例代码前后的 Tree List:

在这里插入图片描述

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));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值