2015-01-26 39 views
1

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

没有递归纯粹是为了做它没有递归的运动 - 我的理解可能是不可能的)

此功能可以节省很多时间重复使用跨大规模的解决方案,编码人员可以使用简单的For Each范例遍历节点。

我已经看到了一种技术,使用C#使用LINQ和递归“扁平化”Nodes集合,但我不确定该语法是否可以完全转换为VB.NET。所以如果有任何聪明的VB函数可以模拟这个任务 - 会非常有用。

还有很多类似的问题,并非常翔实的答案在SO,像这样的: Enumerating Collections that are not inherently IEnumerable? ......这凸显使用某种算法非常深的树木堆栈溢出错误。我希望一个不使用递归的方法不会遭受堆栈溢出错误 - 但是,我已经准备好了,它可能会很长,笨拙和缓慢。

我也准备回答'这是不可能做到这一点,没有递归'。不过,我想确认或拒绝使用的功率SO(本论坛)这种说法

+0

你说你已经知道如何通过'TreeView'控制读取找到的所有节点,那么为什么不能你只是建立一个结果列表?目前还不清楚你遇到什么困难。 – 2015-01-26 18:50:09

+0

@StevenDoggart谢谢,是的,我可以遍历并使用递归构建一个List。这个问题应该强调'不递归' - 如果可能的话。 – Grantly 2015-01-26 18:51:26

+0

您希望列表中的项目位于哪个订单中? – 2015-01-26 19:06:32

回答

2

这是可能的,而且不是很难在所有....

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 
+0

非常好。不像我预期的那样笨拙! – Grantly 2015-01-26 19:12:48

+0

现在您可以先指定宽度或深度。 – 2015-01-26 19:27:33

+0

大声笑,我喜欢你的改善和增加实用性。 (大声笑,因为你看起来像一个编码器谁喜欢延长,使一个真正强大的功能) – Grantly 2015-01-26 19:31:37

1

节点只需添加到列表,但同时保持您处理的最后一个节点的位置。当其直接子项被添加到列表中时,节点被视为进程。

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 

我不确定在使用它之前是否必须在Nodes属性上执行Nothing检查。

注:我能够用的AddRange删除此for循环和更换

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

谢谢,这看起来很整洁。给我几秒来测试它,但它看起来非常好 – Grantly 2015-01-26 19:16:36

+0

@Grantly我删除了For Each并用AddRange替换它 – 2015-01-26 19:21:59

+0

好的改进。干杯。非常有效和整洁 – Grantly 2015-01-26 19:27:02