2015-01-26 38 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