2008-10-07 18 views
14

Mam kontrolkę TreeView w mojej aplikacji WinForms .NET, która ma wiele poziomów obiektów potomnych, które mają węzły z większą liczbą obiektów potomnych, bez zdefiniowanej głębokości. Kiedy użytkownik wybierze dowolny węzeł nadrzędny (niekoniecznie na poziomie katalogu głównego), w jaki sposób mogę uzyskać listę wszystkich węzłów poniżej tego węzła nadrzędnego?Jak uzyskać listę wszystkich węzłów podrzędnych w TreeView w .NET

Na przykład, zacząłem się z tym:

Dim nodes As List(Of String) 

For Each childNodeLevel1 As TreeNode In parentNode.Nodes 
    For Each childNodeLevel2 As TreeNode In childNodeLevel1.Nodes 
     For Each childNodeLevel3 As TreeNode In childNodeLevel2.Nodes 
      nodes.Add(childNodeLevel3.Text) 
     Next 
    Next 
Next 

Problemem jest to, że głębokość pętla jest zdefiniowana, a ja tylko się węzły pochowany w dół na trzech poziomach. Co się stanie, gdy następnym razem, gdy użytkownik wybierze węzeł nadrzędny, będzie siedem poziomów?

Odpowiedz

17

Zastosowanie rekurencji

Function GetChildren(parentNode as TreeNode) as List(Of String) 
    Dim nodes as List(Of String) = New List(Of String) 
    GetAllChildren(parentNode, nodes) 
    return nodes 
End Function 

Sub GetAllChildren(parentNode as TreeNode, nodes as List(Of String)) 
    For Each childNode as TreeNode in parentNode.Nodes 
    nodes.Add(childNode.Text) 
    GetAllChildren(childNode, nodes) 
    Next 
End Sub 
+0

Szybka edycja: Linia 3 powinna zadzwonić do GetAllChildren –

+0

@ [Matt Hanson]: skorygowana linia 3 zgodnie z komentarzami –

+2

Nie sugerowałabym wywoływania tego "GetChildren", ponieważ nie ma po prostu dzieci (tj. Węzłów bezpośrednio pod bieżącym jeden) to także dostaje wnuki, prawnuki i tak dalej. Dla jasności uważam, że lepiej mieć "GetDescendants" dla wszystkich potomków i "GetChildren" tylko na najbliższym niższym poziomie. – Keith

12

trzeba rekurencyjną funkcję to zrobić [lub odpowiednik pętli, ale wersja rekurencyjna jest prostsze] - pseudokod:

function outputNodes(Node root) 
    writeln(root.Text) 
    foreach(Node n in root.ChildNodes) 
     outputNodes(n) 
    end 
end 
+1

ten fragment kodu funkcji rekurencyjnej nauczyli mnie lepiej niż mój nauczyciel w college'u. – Shahin

12

Oto fragment kodu którego używam do wykonania tego zadania z mojej głównej biblioteki. To pozwala na wylistowanie węzłów albo najpierw głębokości albo najpierw oddechu bez użycia rekursji, która ma narzut na konstruowanie stosowych ramek w silniku JIT. Jest bardzo szybki.

go użyć po prostu przejść:

Lista < TreeNode> węzły = TreeViewUtils.FlattenDepth (drzewo);

Przepraszam, że jesteś szefem VB, nie mogę podać przykładu, ale jestem pewien, że to rozwiążesz.

public class TreeViewUtils 
{ 
    /// <summary> 
    /// This static utiltiy method flattens all the nodes in a tree view using 
    /// a queue based breath first search rather than the overhead 
    /// of recursive method calls. 
    /// </summary> 
    /// <param name="tree"></param> 
    /// <returns></returns> 
    public static List<TreeNode> FlattenBreath(TreeView tree) { 
     List<TreeNode> nodes = new List<TreeNode>(); 

     Queue<TreeNode> queue = new Queue<TreeNode>(); 

     // 
     // Bang all the top nodes into the queue. 
     // 
     foreach(TreeNode top in tree.Nodes) { 
      queue.Enqueue(top); 
     } 

     while(queue.Count > 0) { 
      TreeNode node = queue.Dequeue(); 
      if(node != null) { 
       // 
       // Add the node to the list of nodes. 
       // 
       nodes.Add(node); 

       if(node.Nodes != null && node.Nodes.Count > 0) { 
        // 
        // Enqueue the child nodes. 
        // 
        foreach(TreeNode child in node.Nodes) { 
         queue.Enqueue(child); 
        } 
       } 
      } 
     } 

     return nodes; 
    } 

    /// <summary> 
    /// This static utiltiy method flattens all the nodes in a tree view using 
    /// a stack based depth first search rather than the overhead 
    /// of recursive method calls. 
    /// </summary> 
    /// <param name="tree"></param> 
    /// <returns></returns> 
    public static List<TreeNode> FlattenDepth(TreeView tree) { 
     List<TreeNode> nodes = new List<TreeNode>(); 

     Stack<TreeNode> stack = new Stack<TreeNode>(); 

     // 
     // Bang all the top nodes into the queue. 
     // 
     foreach(TreeNode top in tree.Nodes) { 
      stack.Push(top); 
     } 

     while(stack.Count > 0) { 
      TreeNode node = stack.Pop(); 
      if(node != null) { 

       // 
       // Add the node to the list of nodes. 
       // 
       nodes.Add(node); 

       if(node.Nodes != null && node.Nodes.Count > 0) { 
        // 
        // Enqueue the child nodes. 
        // 
        foreach(TreeNode child in node.Nodes) { 
         stack.Push(child); 
        } 
       } 
      } 
     } 

     return nodes; 
    } 

} 
+1

Zajrzę do tego. Dzięki za podzielenie się, Adrian! –

+0

Wreszcie podejście nierekurencyjne. Wielkie dzięki. – ElektroStudios

1

Ik heb de kod omgezet naar vb.net spotkał dit als resultaat ... suc6

Public Function FlattenBreadth(ByVal tree As TreeView) As List(Of TreeNode) 
     Dim nodes As New List(Of TreeNode) 
     Dim queue As New Queue(Of TreeNode) 
     Dim top As TreeNode 
     Dim nod As TreeNode 
     For Each top In tree.Nodes 
      queue.Enqueue(top) 
     Next 
     While (queue.Count > 0) 
      top = queue.Dequeue 
      nodes.Add(top) 
      For Each nod In top.Nodes 
       queue.Enqueue(nod) 
      Next 
     End While 
     FlattenBreadth = nodes 
End Function 
12

Mam metodę rozszerzenia, które mogę używać do tego:

public static IEnumerable<TreeNode> DescendantNodes(this TreeNode input) { 
    foreach (TreeNode node in input.Nodes) { 
     yield return node; 
     foreach (var subnode in node.DescendantNodes()) 
      yield return subnode; 
     } 
} 

To C#, ale można odwoływać się do VB lub konwertować do niego.

0
nodParent As TreeNode 
'nodParent = your parent Node 
tvwOpt.Nodes.Find(nodParent.Name, True) 

Ów to

+0

BTW czym jest tvwOpt? –

2

metoda Adriana to niesamowite. Działa dość szybko i działa lepiej niż podejście rekurencyjne. Zrobiłem tłumaczenie na VB. Wiele się z tego nauczyłem. Mam nadzieję, że ktoś nadal tego potrzebuje.

Aby go użyć po prostu:

Dim FlattenedNodes As List(Of TreeNode) = clTreeUtil.FlattenDepth(Me.TreeView1) 

Oto kod, na zdrowie! :

Public Class clTreeUtil 
''' <summary> 
''' This static utiltiy method flattens all the nodes in a tree view using 
''' a queue based breath first search rather than the overhead 
''' of recursive method calls. 
''' </summary> 
''' <param name="tree"></param> 
''' <returns></returns> 
Public Shared Function FlattenBreath(Tree As TreeView) As List(Of TreeNode) 
    Dim nodes As List(Of TreeNode) = New List(Of TreeNode) 
    Dim queue As Queue(Of TreeNode) = New Queue(Of TreeNode) 

    '' 
    '' Bang all the top nodes into the queue. 
    '' 
    For Each top As TreeNode In Tree.Nodes 
     queue.Enqueue(top) 
    Next 

    While (queue.Count > 0) 
     Dim node As TreeNode = queue.Dequeue() 
     If node IsNot Nothing Then 
      '' 
      '' Add the node to the list of nodes. 
      '' 
      nodes.Add(node) 

      If node.Nodes IsNot Nothing And node.Nodes.Count > 0 Then 
       '' 
       '' Enqueue the child nodes. 
       '' 
       For Each child As TreeNode In node.Nodes 
        queue.Enqueue(child) 
       Next 
      End If 
     End If 
    End While 

    Return nodes 
End Function 

''' <summary> 
''' This static utiltiy method flattens all the nodes in a tree view using 
''' a stack based depth first search rather than the overhead 
''' of recursive method calls. 
''' </summary> 
''' <param name="tree"></param> 
''' <returns></returns> 
Public Shared Function FlattenDepth(tree As TreeView) As List(Of TreeNode) 
    Dim nodes As List(Of TreeNode) = New List(Of TreeNode) 

    Dim stack As Stack(Of TreeNode) = New Stack(Of TreeNode) 

    '' 
    '' Bang all the top nodes into the queue. 
    '' 
    For Each top As TreeNode In tree.Nodes 
     stack.Push(top) 
    Next 

    While (stack.Count > 0) 
     Dim node As TreeNode = stack.Pop() 

     If node IsNot Nothing Then 

      '' 
      '' Add the node to the list of nodes. 
      '' 
      nodes.Add(node) 

      If node.Nodes IsNot Nothing And node.Nodes.Count > 0 Then 
       '' 
       '' Enqueue the child nodes. 
       '' 
       For Each child As TreeNode In node.Nodes 
        stack.Push(child) 
       Next 
      End If 
     End If 

    End While 

    Return nodes 
End Function 

End Class 
0

Jeśli ktoś jeszcze chce zrobić podejście rekursji, przy użyciu kodu Jop, a utrzymanie TreeNodes (dzięki czemu można używać ich .tag, .name, .checked lub właściwości .text) tutaj jest moja wersja

Public Shared Function GetChildren(objTree As TreeView) As List(Of TreeNode) 
    Dim nodes As List(Of TreeNode) = New List(Of TreeNode) 
    For Each parentNode As TreeNode In objTree.Nodes 
     nodes.Add(parentNode) 
     GetAllChildren(parentNode, nodes) 
    Next 

    Return nodes 
End Function 

Public Shared Sub GetAllChildren(parentNode As TreeNode, nodes As List(Of TreeNode)) 
    For Each childNode As TreeNode In parentNode.Nodes 
     nodes.Add(childNode) 
     GetAllChildren(childNode, nodes) 
    Next 
End Sub 
0

Zazwyczaj uzyskanie wartości w określonym węźle jest interesujące dla programistów. Można to uzyskać w następujący sposób.Zakłada się, że masz kontroli TextBox o nazwie texbox1 i kontrolę TreeView nazwie treeview1.Following wróci wartość tekstu w węzłach poziomie 0.

textbox1.Text = treeview1.nodes(0).Text.ToString() 
Powiązane problemy