Title: Yield checked TreeView nodes in C#
The previous example used recursion to traverse the nodes in a TreeView control and fill a list with the nodes that are checked. This example uses a slightly different approach. It traverses the TreeView control's nodes and uses the yield return statement to return the checked TreeView nodes. The main program can then iterate over the returned nodes.
The following method recursively crawls through the nodes in a TreeNodeCollection and yields the checked TreeView nodes.
// Return a list of the TreeNodes that are checked.
private IEnumerable<TreeNode> CheckedNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
// Yield this node.
if (node.Checked) yield return node;
// Yield the checked descendants of this node.
foreach (TreeNode checked_child in CheckedNodes(node.Nodes))
yield return checked_child;
}
}
The method loops through the TreeNodeCollection. If a node is checked, it uses yield return to return that node.
The method then calls itself recursively to find the checked descendants of that node that are in its Nodes collection. It iterates over those descendants and uses yield return to return them.
This is one of the more common complaints about the yield return syntax: it doesn't allow you to return an IEnumerable in a single statement. For example, it might be nice to be able to do something like yield return CheckedNodes(nodes.Nodes).
Unfortunately you can't do that. Instead you must iterate through another IEnumerable and return each item individually.
If you think about the way yield works, however, you'll realize that it doesn't make much difference in terms of performance. The yield statement returns a value to the loop that is iterating over its values and then suspends its own execution until the next time a value is needed. Being able to return an IEnumerable in a single statement might be syntactically convenient but it wouldn't change the fact that the program needs to wait between each returned value.
But back to this example. To make it easier to find the control's checked TreeView nodes, the program defines the following method.
// Return a list of the checked TreeView nodes.
private IEnumerable<TreeNode> CheckedNodes(TreeView trv)
{
return CheckedNodes(trv.Nodes);
}
This method simply invokes the previous one to find the checked TreeView nodes in the control's Nodes collection.
Notice that this method simply returns the result of the call to the other CheckedNodes method without using yield return. If a method uses yield return, then Visual Studio knows that the method is an iterator and it can only return values with yield return.
That's not the case here, however. This method doesn't use yield return so it's allowed to use a normal return statement to return the IEnumerable result of the method call.
Finally the main program uses the following code to invoke this method.
// List the checked TreeNodes.
private void btnShowChecked_Click(object sender, EventArgs e)
{
string results = "";
foreach (TreeNode node in CheckedNodes(trvMeals))
results += node.Text + "\n";
MessageBox.Show(results);
}
This code iterates through the nodes returned by the preceding CheckedNodes method, adding their text to a string. When it's done, the code displays the string.
Download the example to experiment with it and to see additional details.
|