views:

11834

answers:

6

I have a TreeView control in my WinForms .NET application that has multiple levels of childnodes that have childnodes with more childnodes, with no defined depth. When a user selects any parent node (not necessarily at the root level), how can I get a list of all the nodes beneith that parent node?

For example, I started off with this:

Dim nodes As List(Of String)

For Each childNodeLevel1 As TreeNode In parentNode.Nodes
    For Each childNodeLevel2 As TreeNode In childNodeLevel1.Nodes
        For Each childNodeLevel3 As TreeNode In childNodeLevel2.Nodes
            nodes.Add(childNodeLevel3.Text)
        Next
    Next
Next

The problem is that this loop depth is defined and I'm only getting nodes burried down three levels. What if next time the user selects a parent node, there are seven levels?

+6  A: 

you need a recursive function to do this [or a loop equivalent, but the recursive version is simpler] - pseudocode:

function outputNodes(Node root)
    writeln(root.Text)
    foreach(Node n in root.ChildNodes)
        outputNodes(n)
    end
end
Steven A. Lowe
+3  A: 

Use recursion (sorry, my onboard vb.net syntax checker is weak)

Function GetChildren(parentNode as TreeNode) as List(Of String)
  Dim nodes as List(Of String)
  GetAllChildren(parentNode, nodes)
  return nodes
End

Sub GetAllChildren(parentNode as TreeNode, nodes as List(Of String))
  For Each childNode as TreeNode in parentNode.Nodes
    nodes.Add(childNode.Text)
    GetAllChildren(childNode, nodes)
  Next
End
jop
Just a quick edit: Line 3 should call GetAllChildren
Matt Hanson
@[Matt Hanson]: corrected line 3 as per comments
Steven A. Lowe
I'd suggest not calling this `GetChildren` as it doesn't just get children (i.e. nodes directly under the current one) it also gets grandchildren, great grandchildren, and so on. For clarity I find it better to have `GetDescendants` for all descendants and `GetChildren` for just the immediate level below.
Keith
+5  A: 

Here is a snippet of code that I use to perform this task from my core library. It allows you to list the nodes either depth first or breath first without the use of recursion, which has the overhead of constructing stackframes in the JIT engine. Its very fast.

To use it simply go:

List< TreeNode > nodes = TreeViewUtils.FlattenDepth(tree);

Sorry you you VB heads, I can't give an example, but I'm sure you'll work it out.

public class TreeViewUtils
{
    /// <summary>
    /// This static utiltiy method flattens all the nodes in a tree view using
    /// a queue based breath first search rather than the overhead
    /// of recursive method calls.
    /// </summary>
    /// <param name="tree"></param>
    /// <returns></returns>
    public static List<TreeNode> FlattenBreath(TreeView tree) {
        List<TreeNode> nodes = new List<TreeNode>();

        Queue<TreeNode> queue = new Queue<TreeNode>();

        //
        // Bang all the top nodes into the queue.
        //
        foreach(TreeNode top in tree.Nodes) {
            queue.Enqueue(top);
        }

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

        return nodes;
    }

    /// <summary>
    /// This static utiltiy method flattens all the nodes in a tree view using
    /// a stack based depth first search rather than the overhead
    /// of recursive method calls.
    /// </summary>
    /// <param name="tree"></param>
    /// <returns></returns>
    public static List<TreeNode> FlattenDepth(TreeView tree) {
        List<TreeNode> nodes = new List<TreeNode>();

        Stack<TreeNode> stack = new Stack<TreeNode>();

        //
        // Bang all the top nodes into the queue.
        //
        foreach(TreeNode top in tree.Nodes) {
            stack.Push(top);
        }

        while(stack.Count > 0) {
            TreeNode node = stack.Pop();
            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) {
                        stack.Push(child);
                    }
                }
            }
        }

        return nodes;
    }

}
Adrian Regan
I'll look into it. Thanks for sharing, Adrian!
Matt Hanson
+1  A: 

Ik heb de code omgezet naar vb.net met dit als resultaat... suc6

Public Function FlattenBreath(ByVal tree As TreeView) As List(Of TreeNode)
        Dim nodes As New List(Of TreeNode)
        Dim queue As New Queue(Of TreeNode)
        Dim top As TreeNode
        Dim nod As TreeNode
        For Each top In tree.Nodes
            queue.Enqueue(top)
        Next
        While (queue.Count > 0)
            top = queue.Dequeue
            nodes.Add(top)
            For Each nod In top.Nodes
                queue.Enqueue(nod)
            Next
        End While
        EnumTreeview = nodes
End Function
joost
+4  A: 

I have an extension method that I use for this:

public static IEnumerable<TreeNode> DescendantNodes( this TreeNode input ) {
    foreach ( TreeNode node in input.Nodes ) {
        yield return node;
        foreach ( var subnode in node.DescendantNodes() )
            yield return subnode;
        }
}

It's C#, but could be referenced from VB or converted to it.

Keith
A: 

nodParent As TreeNode 'nodParent = your parent Node tvwOpt.Nodes.Find(nodParent.Name, True)

Thats it

Sunil