2009-09-03 35 views
8

Ho un controllo TreeView con set di nodi e nodi figlio. Ad esempio:Accesso a tutti i nodi in TreeView Control

ROOT ha A, B, C.

A ha a1, a2, a3 e quindi a1, a2 contiene anche nodi come x1, x2, x3 e così via. Come questo ci sono molti sottonodi. So che è possibile utilizzare i loop con un ciclo for.

Voglio solo accedere a tutti i nodi nel controllo TreeView utilizzando uno o due cicli.

Esiste un algoritmo o esiste un altro modo?

Un'altra domanda: è possibile avere il percorso di un nodo dell'albero in un oggetto o in una stringa utilizzando qualsiasi funzione di libreria? Per esempio:

string S = TreeView1.Nodes[i].Nodes[j].Nodes 

risposta

9

Non utilizzare cicli annidati, ma andare per una soluzione ricorsiva come:

void ListNodes(TreeNode node) 
{ 
    foreach(var subnode in node.Nodes) 
    { 
    ListNodes(subnode); 
    } 
    // Print out node 
} 

chiamare questa funzione per il nodo principale.

Per la domanda aggiuntiva: verificare la proprietà FullPath.

5

È possibile utilizzare una funzione ricorsiva per attraversare l'intero albero:

private void Traverse(TreeNodeCollection nodes) 
{ 
    foreach (TreeNode node in nodes) 
    { 
     Console.WriteLine("{0} -> {1}", node.Name, node.FullPath); 
     Traverse(node.Nodes); 
    } 
} 

È quindi possibile chiamare questo utilizzando:

Traverse(treeView.Nodes); 

e camminerete prima tutta la profondità dell'albero (vale a dire andare. il più profondo possibile prima di passare al fratello successivo). Passare nella collezione Nodes significa che questo codice si occuperà di alberi con più nodi radice.

Il codice di esempio qui sopra stamperà il nome del nodo e il percorso completo di quel nodo all'interno dell'albero.

5

Non sono il più grande fan della ricorsione, ma sembra che tu debba usarlo. Ho visto un esempio geniale online mescolando la ricorsione con un iteratore.

private int GetLevels(TreeNodeCollection treeNodes) 
    { 
     int level = 0; 
     foreach (TreeNode node in TreeTopDown(treeNodes)) 
     { 
      int i = node.Level; 
      if (i > level) level = i; 
     } 
     return level; 
    } 

    //TopDown Iterator 
    private IEnumerable<TreeNode> TreeTopDown(TreeNodeCollection treeNodes) 
    { 
     foreach (TreeNode node in treeNodes) 
     { 
      yield return node; 
      foreach (TreeNode subNode in TreeTopDown(node.Nodes)) yield return subNode;    
     } 
    } 

    //BottomUp Iterator 
    private IEnumerable<TreeNode> TreeBottomUp(TreeNodeCollection treeNodes) 
    { 
     foreach (TreeNode node in treeNodes) 
     { 
      foreach (TreeNode subNode in TreeBottomUp(node.Nodes)) yield return subNode; 
      yield return node; 
     } 
    } 
1

È possibile utilizzare coda come quello che avevo fatto nella mia domanda:

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

// 
// first insert all the root nodes into the queue. 
// 
foreach(TreeNode root in tree.Nodes) { 
    queue.Enqueue(root); 
} 

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); 
      } 
     } 
    } 
} 
2

So che questa discussione è piuttosto vecchio e il mio metodo non esattamente ridurre la quantità di ricorsione e esso potrebbe essere leggermente più lento ma rende il mio codice un po 'più pulito.

io uso un metodo di estensione per IEnumarable<> per appiattire qualsiasi albero (non solo i nodi TreeView):

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> rootNodes, 
    Func<T, IEnumerable<T>> childrenFunction) 
{ 
    return rootNodes.SelectMany(
     child => new[] { child } 
      .Concat((childrenFunction(child) ?? Enumerable.Empty<T>()) 
      .Flatten(childrenFunction))); 
} 

Ho quindi utilizzare questo metodo per ottenere tutti i nodi della struttura:

IEnumerable<TreeNode> allNodes = treeView1.Nodes.Cast<TreeNode>() 
    .Flatten<TreeNode>(n => n.Nodes.Cast<TreeNode>()); 
+0

questo sembra molto ordinato ... Può essere convertito in VB.NET? VB può usare l'operatore =>? – Grantly

3

È possibile creare un metodo di estensione che restituisce un List<TreeNode>.

Discendenti Metodo di estensione

using System.Linq; 
using System.Windows.Forms; 
using System.Collections.Generic; 

public static class Extensions 
{ 
    public static List<TreeNode> Descendants(this TreeView tree) 
    { 
     var nodes = tree.Nodes.Cast<TreeNode>(); 
     return nodes.SelectMany(x => x.Descendants()).Concat(nodes).ToList(); 
    } 

    public static List<TreeNode> Descendants(this TreeNode node) 
    { 
     var nodes = node.Nodes.Cast<TreeNode>().ToList(); 
     return nodes.SelectMany(x => Descendants(x)).Concat(nodes).ToList(); 
    } 
} 

Per ottenere tutti i nodi di un TreeView

var nodes = this.treeView1.Descendants(); 

Per ottenere tutti i nodi figli di un nodo

var nodes = this.treeView1.Nodes[0].Descendants(); 

È possibile anche usare LINQ per cercare tra i nodi.

0

Il seguente codice viene utilizzato per attraversare i nodi di un TreeView e ritorno solo nodi foglia:

private IEnumerable<TreeNode> LeafNodes(TreeNode root) 
{ 
    Stack<TreeNode> stack = new Stack<TreeNode>(); 
    stack.Push(root); 
    while (stack.Count > 0) 
    { 
     TreeNode current = stack.Pop(); 
     if (current.Nodes.Count == 0) 
     { 
      yield return current; 
     } 
     else 
     { 
      foreach (TreeNode child in current.Nodes) 
      { 
       stack.Push(child); 
      } 
     } 
    } 
} 

Io lo uso per accedere ai nomi dei file in un esploratore-come TreeView:

private void LogFileNames() 
{ 
    //There may be more than one node at root level 
    foreach (TreeNode rootNode in FileTreeView.Nodes) 
    { 
     //Print only filenames, not directories 
     foreach (TreeNode leafNode in LeafNodes(rootNode)) 
     { 
      Logger.Info(leafNode.Text); 
     } 
    } 
} 
Problemi correlati