1

Hopefully this is not a repeat question. I looked and found similar questions but not sufficient in this case. I have many tree view controls, and can traverse the Nodes recursively for various reasons. However, I often need to traverse the Nodes as if they are in a List. I would like to make a function that creates a Generic.List(of TreeNode) from the Nodes collection, without recursion if at all possible.

(Without recursion purely for the exercise of doing it without recursion - I understand it may not be possible)

This function would save alot of time with repeated use across a massive solution, where the coders could use a simple For Each paradigm to traverse the Nodes.

I have seen a technique to 'flatten' the Nodes collection using C#, which uses LINQ and recursion, but I am not sure that the syntax can be converted to VB.NET cleanly. So if there are any clever VB functions out there can that I can mold to this task - would be very helpful.

There are many similar questions and very informative answers on SO, like this one: Enumerating Collections that are not inherently IEnumerable? ...which highlights stack overflow errors in very deep trees using some algorithms. I hope that a method that does not use recursion will not suffer from Stack overflow errors - however, I am prepared that it might be long and clumsy and slow.

I am also prepared for the answer that 'It is not possible to do this without recursion'. However, I would like to confirm or deny this claim using the power of SO (this forum)

Community
  • 1
  • 1
Grantly
  • 2,546
  • 2
  • 21
  • 31
  • You say you already know how to read through the `TreeView` control to find all the nodes, so why can't you just build a list with the results? It's not clear what part of the task you are having trouble with. – Steven Doggart Jan 26 '15 at 18:50
  • @StevenDoggart Thanks, yes I can traverse and build a List using recursion. The question is supposed to highlight 'no recursion' - if at all possible. – Grantly Jan 26 '15 at 18:51
  • In which order do you want the items in the list to be in? – the_lotus Jan 26 '15 at 19:06
  • @the_lotus .. I had not considered the order. For this exercise, the order does not matter. – Grantly Jan 26 '15 at 19:07
  • That's much more clear now. Thanks for clarifying your question. Unfortunately I got pulled away from my desk and others beat me to it :) – Steven Doggart Jan 26 '15 at 19:13
  • @StevenDoggart Thanks anyway, there are some quick responders here. By the looks of it - there are many effective answers – Grantly Jan 26 '15 at 19:26

2 Answers2

2

It's possible, and not very hard at all....

Public Class Form1

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        TreeView1.ExpandAll()
        For Each TN As TreeNode In TreeView1.NodesToListWithoutRecursionBecauseWhyNot(TraverseType.BreadthFirst)
            Debug.Print(TN.Text)
        Next
    End Sub

End Class

Public Module Extensions

    Public Enum TraverseType
        BreadthFirst
        DepthFirst
    End Enum

    <Runtime.CompilerServices.Extension()> _
    Public Function NodesToListWithoutRecursionBecauseWhyNot(ByVal TV As TreeView, Optional ByVal Traverse As TraverseType = TraverseType.DepthFirst) As List(Of TreeNode)
        Dim nodes As New List(Of TreeNode)

        Select Case Traverse
            Case TraverseType.BreadthFirst
                Dim Q As New Queue(Of TreeNode)
                For Each TN As TreeNode In TV.Nodes
                    Q.Enqueue(TN)
                Next

                While Q.Count > 0
                    Dim TN As TreeNode = Q.Dequeue
                    nodes.Add(TN)
                    For Each subTN As TreeNode In TN.Nodes
                        Q.Enqueue(subTN)
                    Next
                End While

            Case TraverseType.DepthFirst
                Dim L As New List(Of TreeNode)
                For Each TN As TreeNode In TV.Nodes
                    L.Add(TN)
                Next

                While L.Count > 0
                    Dim TN As TreeNode = L.Item(0)
                    L.RemoveAt(0)
                    nodes.Add(TN)
                    For i As Integer = TN.Nodes.Count - 1 To 0 Step -1
                        L.Insert(0, TN.Nodes(i))
                    Next
                End While
        End Select

        Return nodes
    End Function

End Module
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40
1

Just add the nodes to the list but at the same time keep the position of the last node you processed. A node is considered process when its immediate children are added to the list.

Public Function GetAllNodes(ByVal topNode As TreeNode)

    Dim allNodes As New List(Of TreeNode)
    Dim lastProcessPosition As Integer = 0

    allNodes.Add(topNode)

    Do While lastProcessPosition < allNodes.Count
        allNodes.AddRange(allNodes(lastProcessPosition).Nodes)

        lastProcessPosition += 1
    Loop

    Return allNodes
End Function

If you don't have a top node then just substitute the parameter for a list of nodes to start with.

Public Function GetAllNodes(ByVal topNodes As TreeNodeCollection)

    Dim allNodes As New List(Of TreeNode)
    Dim lastProcessPosition As Integer = 0

    allNodes.AddRange(topNodes)

    Do While lastProcessPosition < allNodes.Count
        allNodes.AddRange(allNodes(lastProcessPosition).Nodes)

        lastProcessPosition += 1
    Loop

    Return allNodes
End Function

I'm not sure if a check for Nothing must be done on the Nodes property before using it.

Note: I was able to remove this for loop and replace it with AddRange

'For Each node As TreeNode In allNodes(lastProcessPosition).Nodes
'    allNodes.Add(node)
'Next
the_lotus
  • 12,668
  • 3
  • 36
  • 53