无需递归将 Treeview 节点集合展平为列表

Flatten Treeview Nodes collection to List without recursion

希望这不是一个重复的问题。我查看并发现了类似的问题,但在这种情况下还不够。 我有很多树视图控件,并且由于各种原因可以递归遍历节点。 但是,我经常需要遍历节点,就好像它们在列表中一样。 我想创建一个函数,从 Nodes 集合中创建一个 Generic.List(of TreeNode) 没有递归 如果可能的话。

(不加递归纯粹是为了练习不加递归做的-我理解可能 不可能)

此函数可以在大规模解决方案中重复使用,从而节省大量时间,其中编码人员可以使用简单的 For Each 范例来遍历节点。

我见过一种使用 C# 'flatten' Nodes 集合的技术,它使用 LINQ 和递归,但我不确定语法是否可以完全转换为 VB.NET。因此,如果有任何聪明的 VB 函数可以帮助我完成这项任务 - 将会非常有帮助。

关于 SO 有很多类似的问题和非常有用的答案,比如这个: Enumerating Collections that are not inherently IEnumerable? ...使用某些算法突出显示非常深的树中的堆栈溢出错误。我希望不使用递归的方法不会出现 Stack overflow 错误 - 但是,我已经准备好它可能又长又笨又慢。

我也准备好回答“没有递归就不可能做到这一点”。但是,我想借助 SO(本论坛)

的力量来确认或否认这种说法

有可能,而且一点也不难....

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

只需将节点添加到列表中,但同时保留您处理的最后一个节点的位置。当一个节点的直接子节点被添加到列表中时,该节点被认为是进程。

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

如果您没有顶级节点,则只需将参数替换为开始的节点列表。

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

我不确定在使用之前是否必须在节点 属性 上检查 Nothing。

注意:我能够删除这个 for 循环并将其替换为 AddRange

'For Each node As TreeNode In allNodes(lastProcessPosition).Nodes
'    allNodes.Add(node)
'Next