访问TreeView控件中的所有节点

9
我有一个带有节点和子节点的TreeView控件,例如:
ROOT有A,B,C。
A有a1、a2、a3,然后a1、a2也包含一些节点,如x1、x2、x3等。像这样还有许多子节点。我知道可以使用循环与for循环一起使用。
我只想使用一个或两个for循环来访问TreeView控件中的所有节点。
是否有任何算法可用于此,或者是否有其他方法?
另外一个问题:是否可能使用任何库函数将树节点的路径存储在对象或字符串中?例如:
string S = TreeView1.Nodes[i].Nodes[j].Nodes
7个回答

10

不要使用嵌套循环,而要选择递归解决方案,例如:

void ListNodes( TreeNode node )
{
  foreach( var subnode in node.Nodes )
  {
    ListNodes( subnode );
  }
  // Print out node
}

调用该函数以处理您的根节点。

对于您的附加问题:请检查FullPath属性。


8
您可以使用递归函数遍历整棵树:
private void Traverse(TreeNodeCollection nodes)
{
    foreach (TreeNode node in nodes)
    {
        Console.WriteLine("{0} -> {1}", node.Name, node.FullPath);
        Traverse(node.Nodes);
    }
}

您可以使用以下方式调用此功能:
Traverse(treeView.Nodes);

它会深度优先遍历整个树(即在移动到下一个兄弟节点之前尽可能向下走)。传递Nodes集合意味着此代码将处理具有多个根节点的树。

上面的示例代码将打印出节点的名称以及该节点在树中的完整路径


5

我并不是递归的忠实粉丝,但看起来你必须使用它。我在网上看到一个聪明的例子,将迭代器与递归混合使用。

    private int GetLevels(TreeNodeCollection treeNodes)
    {
        int level = 0;
        foreach (TreeNode node in TreeTopDown(treeNodes))
        {
            int i = node.Level;
            if (i > level) level = i;
        }
        return level;
    }

    //TopDown Iterator 
    private IEnumerable<TreeNode> TreeTopDown(TreeNodeCollection treeNodes)
    {
        foreach (TreeNode node in treeNodes)
        {
            yield return node;
            foreach (TreeNode subNode in TreeTopDown(node.Nodes)) yield return subNode;               
        }
    }

    //BottomUp Iterator
    private IEnumerable<TreeNode> TreeBottomUp(TreeNodeCollection treeNodes)
    {
        foreach (TreeNode node in treeNodes)
        {
            foreach (TreeNode subNode in TreeBottomUp(node.Nodes)) yield return subNode;
            yield return node;
        }
    }

3
你可以创建一个扩展方法,返回一个List<TreeNode>。关于扩展方法的更多信息,请参考扩展方法后代扩展方法
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;

public static class Extensions
{
    public static List<TreeNode> Descendants(this TreeView tree)
    {
        var nodes = tree.Nodes.Cast<TreeNode>();
        return nodes.SelectMany(x => x.Descendants()).Concat(nodes).ToList();
    }

    public static List<TreeNode> Descendants(this TreeNode node)
    {
        var nodes = node.Nodes.Cast<TreeNode>().ToList();
        return nodes.SelectMany(x => Descendants(x)).Concat(nodes).ToList();
    }
}

获取TreeView的所有节点:
var nodes = this.treeView1.Descendants();

获取一个节点的所有子节点
var nodes = this.treeView1.Nodes[0].Descendants();

你也可以使用linq来搜索节点之间的关系。

祖先扩展方法

要获取节点的祖先,你可能还会对祖先扩展方法感兴趣。


2

我知道这个线程很旧,而且我的方法并没有减少递归的数量,可能稍微慢了一点,但它使我的代码更加简洁。

我使用一个扩展方法来扁平化任何树(不仅仅是TreeView节点):

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> rootNodes, 
    Func<T, IEnumerable<T>> childrenFunction)
{
    return rootNodes.SelectMany(
        child => new[] { child }
            .Concat((childrenFunction(child) ?? Enumerable.Empty<T>())
            .Flatten(childrenFunction)));
}

我随后使用这种方法获取树的所有节点:
IEnumerable<TreeNode> allNodes = treeView1.Nodes.Cast<TreeNode>()
    .Flatten<TreeNode>(n => n.Nodes.Cast<TreeNode>());

这看起来非常整洁...它能转换成VB.NET吗?VB可以使用=>运算符吗? - Grantly

1
您可以像我在我的应用程序中所做的那样使用队列:
List<TreeNode> nodes = new List<TreeNode>();
Queue<TreeNode> queue = new Queue<TreeNode>();

//
// first insert all the root nodes into the queue.
//
foreach(TreeNode root in tree.Nodes) {
    queue.Enqueue(root);
}

while(queue.Count > 0) {
    TreeNode node = queue.Dequeue();
    if(node != null) {
        //
        // Add the node to the list of nodes.
        //
        nodes.Add(node);

        if(node.Nodes != null && node.Nodes.Count > 0) {
            //
            // Enqueue the child nodes.
            //
            foreach(TreeNode child in node.Nodes) {
                queue.Enqueue(child);
            }
        }
    }
}

0
以下代码用于遍历TreeView的节点并仅返回叶子节点:
private IEnumerable<TreeNode> LeafNodes(TreeNode root)
{
    Stack<TreeNode> stack = new Stack<TreeNode>();
    stack.Push(root);
    while (stack.Count > 0)
    {
        TreeNode current = stack.Pop();
        if (current.Nodes.Count == 0)
        {
            yield return current;
        }
        else
        {
            foreach (TreeNode child in current.Nodes)
            {
                stack.Push(child);
            }
        }
    }
}

我用它来访问类似资源管理器的TreeView中的文件名:

private void LogFileNames()
{
    //There may be more than one node at root level
    foreach (TreeNode rootNode in FileTreeView.Nodes)
    {
        //Print only filenames, not directories
        foreach (TreeNode leafNode in LeafNodes(rootNode))
        {
            Logger.Info(leafNode.Text);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接