Title: Enumerate TreeView nodes in C#
The previous two posts show two ways you can enumerate TreeView nodes that are checked. The idea is to recursively crawl over the tree's nodes and pick out the checked ones.
Unfortunately that method isn't easily extendable. For example, if you wanted to find the nodes that were expanded, you would need to write new code to do that. The new code would be almost the same as the old code but with a different test to see whether a node should be included in the result.
This example shows how you can enumerate TreeView nodes to examine every node. Then the main program can test to see which nodes it needs to use.
To make using the code easier, the example defines three extension methods for the TreeView class.
The first two are shown in the following code. They work together so, to make it easier to tell which one is which, I've color coded their calls to each other.
// Return the nodes in this collection and their descendants.
public static IEnumerable<TreeNode> Descendants(
this TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
yield return node;
foreach (TreeNode child in node.Nodes.Descendants())
{
yield return child;
}
}
}
// Return this node and its descendants.
public static IEnumerable<TreeNode> Descendants(this TreeNode node)
{
yield return node;
foreach (TreeNode child in node.Nodes.Descendants())
{
yield return child;
}
}
The first (blue) method works on a TreeNodeCollection. It loops through the nodes in the collection and calls the second (red) method for each.
The first (blue) method works on a TreeNodeCollection. It loops through the nodes in the collection and yields each node for the enumeration. It then calls the second (red) method for each node's Nodes collection to enumerate the node's descendants. It loops through the returned nodes and yields them.
The second (red) method works on a single TreeNode. It first yields the node. Then it uses the first (blue) method to enumerate the node's descendants. It loops through the descendants and yields them.
This highlights a common complaint about the yield statement. There's no easy way to return the results of another IEnumerable method. Instead you need to iterate through it and return each of the values individually.
This example also defines the following extension method for the TreeView control.
// Return all of the TreeView's nodes.
public static IEnumerable<TreeNode> Descendants(this TreeView trv)
{
foreach (TreeNode node in trv.Nodes.Descendants())
yield return node;
}
This method simply invokes the earlier (blue) one to enumerate TreeView nodes in the control's Nodes collection.
This is probably the most useful of the three extension methods. In fact you could make the other two private so they would not be usable outside of the class that defines them.
Now that the program has a way to enumerate TreeView nodes, it uses code similar to the following to find the nodes that are checked.
// List the checked TreeNodes.
private void btnShowChecked_Click(object sender, EventArgs e)
{
string results = "";
foreach (TreeNode node in trvMeals.Descendants())
{
if (node.Checked) results += node.Text + "\n";
}
MessageBox.Show(results);
}
This code uses the last (green) extension method to enumerate the control's nodes. If a node is checked, it adds it to a string. When it's finished, the program displays the string.
You might wonder whether this is as efficient as the methods used by the earlier posts that test each node to see if it is checked before adding it to the enumeration. In either case you need visit every node and test every node to see if it's checked, so the number of steps is the same. The earlier versions return fewer nodes in the enumeration and there's probably some overhead for yielding a node, so they may be a bit more efficient, but the difference shouldn't be huge.
Download the example to experiment with it and to see additional details.
|