Grantly
Grantly

Reputation: 2556

Flatten Treeview Nodes collection to List without recursion

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)

Upvotes: 1

Views: 1494

Answers (2)

Idle_Mind
Idle_Mind

Reputation: 39122

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

Upvotes: 2

the_lotus
the_lotus

Reputation: 12748

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

Upvotes: 1

Related Questions