views:

976

answers:

4

How can one easily iterate through all nodes in a TreeView, examine their .Checked property and then delete all checked nodes?

It seems straightforward, but you aren't supposed to modify a collection through which you are iterating, eliminating the possibility of a "foreach" loop. (The .Nodes.Remove call is modifying the collection.) If this is attempted, the effect is that only about half of the .Checked nodes are removed.

Even if one were to use two passes: first creating a list of temporary indexes, and then removing by index on the second pass -- the indexes would change upon each removal, invaliding the integrity of the index list.

So, what is the most efficient way to do this?

Here is an example of code that looks good, but actually only removes about half of the .Checked nodes.:

            foreach (TreeNode parent in treeView.Nodes)
            {
                if (parent.Checked)
                {
                    treeView.Nodes.Remove(parent);
                }
                else
                {
                    foreach (TreeNode child in parent.Nodes)
                    {
                        if (child.Checked) parent.Nodes.Remove(child);
                    }
                }
            }

(Yes, the intention is only to prune nodes from a tree that is two levels deep.)

+1  A: 

Whilst iterating you could construct a new list of unchecked items and then re-bind your treeview to that new list (discarding the old one).

SnOrfus
+5  A: 

Try walking through the nodes backwards. That way your index doesn't increase past your node size:

for( int ndx = nodes.Count; ndx > 0; ndx--)
{
  TreeNode node = nodes[ndx-1];
  if (node.Checked)
  {
     nodes.Remove(node);
  }
   // Recurse through the child nodes...
}
Jay Mooney
This is the most efficient method.
Romias
+3  A: 

This will remove the nodes after enumerating them, and can be used recursively for n-tiers of nodes.

void RemoveCheckedNodes(TreeNodeCollection nodes)
{
    List<TreeNode> checkedNodes = new List<TreeNode>();

    foreach (TreeNode node in nodes)
    {
        if (node.Checked)
        {
            checkedNodes.Add(node);
        }
        else
        {
            RemoveCheckedNodes(nodes.ChildNodes);
        }
    }

    foreach (TreeNode checkedNode in checkedNodes)
    {
        nodes.Remove(checkedNode);
    }
}
Jon B
+2  A: 

If you want to do it efficiently you need to keep track of the checked nodes as they are checked. Store the checked tree nodes in a list (and remove them as they are unchecked).

If you have a unique key and a LOT of nodes to keep track of you might consider a dictionary as well. But if you are only dealing with 10-50 it probably wont make a big difference.

Then, instead of looping thru the entire tree you just loop thru your (smaller) list of nodes.

Chris Brandsma