3

First off we have the almighty code!

List nodes = new List();
TreeNode Temp = new TreeNode();

TreeNodeCollection nodeList = treeViewTab4DirectoryTree.Nodes;

while (nodeList.Count != 0)
{
    Temp = nodeList[0];

    while (Temp.FirstNode != null)
    {
        Temp = Temp.FirstNode;
    }

    if (!nodes.Contains(Temp.FullPath))
    {
        nodes.Add(Temp.Text);
    }

    nodeList.Remove(Temp);
}

Now the problem: I have written the code above with the intent of creating a List containing the text from all the nodes in the tree. That works perfectly. The problem I am having is when I remove the nodes from my variable they are also being removed from the actual list. The question would be how can I make a copy of the list of nodes so I can play with them without messing with the ACTUAL list. How do I make a copy of it without just making a reference to it? Any help will be greatly appreciated!

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Christopher B. Adkins
  • 3,499
  • 2
  • 26
  • 29
  • FYI: On line 2, you don't need to create a new `TreeNode` -- you can just assign that variable `null` to start with. – Jon Seigel Feb 24 '10 at 13:50
  • @Jon Thanks. I have made the change in my code – Christopher B. Adkins Feb 24 '10 at 15:07
  • Is this a standard WinForms TreeView ? Surely you must have meant to use : List nodes = new List(); ? ... There's also no need to initialize 'Temp to anything : you can just use : TreeNode Temp; – BillW Feb 24 '10 at 15:08
  • 1
    If you are iterating over a standard WinForms Treeview you may find the discussion and sample code here using Linq valuable : http://stackoverflow.com/questions/1815497/enumerating-collections-that-are-not-inherently-ienumerable – BillW Feb 24 '10 at 15:39

2 Answers2

3

Your problem arises because "nodeList" is a reference to treeViewTab4DirectoryTree.Nodes, rather than a copy of it.

The solution depends entirely on the what type of TreeNodeCollection you're using (WinForms, ASP.net, something else?), as you'll need to look for a .Copy(), .Clone(), .ToArray() method or similar to take a copy of the contents of the collection, rather than a reference to the existing collection.

If, for example, you're using asp.net and thus the System.Web.UI.WebControls.TreeNodeCollection, you could use the .CopyTo method in a way similar to this:

TreeNode[] x = null;
treeViewTab4DirectoryTree.Nodes.CopyTo(x, 0);
Rob
  • 45,296
  • 24
  • 122
  • 150
  • @Rob I tried your code but the compiler told be that the destination array can't be null, and it also can't be unassigned. Is there any way to make it dynamic? Would a list work if the signature for CopyTo specifically calls for an Array? – Christopher B. Adkins Feb 24 '10 at 14:53
  • 1
    @Adkins, You'll probably have to initialise the array prior to using it then, the specifics of that will be down to the TreeNode type you're using. Essentially the thrust of my answer is that you need to make a *new* collection to remove items from, rather than removing them from nodeList or treeViewTab4DirectoryTree.Nodes. Instead, could you have a List that you **add** the nodes that you want to keep to (or the ones you want to remove), rather than attempting to modify an existing collection? – Rob Feb 24 '10 at 15:03
  • I can't implicitly convert between the types TreeNodeCollection and a collection of TreeNode elements (? huh ?). I can however foreach through the TreeNodeCollection adding each one to the collection of TreeNode's. Thanks for the pointers! – Christopher B. Adkins Feb 24 '10 at 15:10
2

Updated to show stack based approach:

List<String> result = new List<String>();
Stack<IEnumerator> nodeColls = new Stack<IEnumerator>();
IEnumerator nodes = treeViewTab4DirectoryTree.Nodes.GetEnumerator();

nodeColls.Push(null);

while (nodes != null)
{
    while (nodes.MoveNext())
    {
        result.add(nodes.Current.FullPath);
        if (nodes.Current.FirstNode != null)
        {
            nodeColls.Push(nodes);
            nodes = nodes.Current.Nodes.GetEnumerator();
        }
    }

    nodes = nodeColls.Pop();
}

The code below does not work as was mentioned in comments, because it doesn't traverse the entire tree, but only takes the first leaf node of each top-level branch.

I actually thought the original code (in the question) did so too, because I thought the Remove would actually remove the top-level node after finding the first leaf node under it; but instead, it tries to remove the leaf node from the collection of top-level nodes, and just ignores it if it can't find it.

Original post, non-functioning code

First of all, why do you need to remove the items from your list?

List<string> nodes = new List<string>();

foreach (TreeNode tn in treeViewTab4DirectoryTree.Nodes)
{
    TreeNode temp = tn;

    while (Temp.FirstNode != null)
    {
        Temp = Temp.FirstNode;
    }

    if (!nodes.Contains(Temp.FullPath))
    {
        nodes.Add(Temp.Text);
    }
}

To answer your concrete question, assuming the Nodes collection implements IEnumerable, use:

List<TreeNode> nodeList = new List<TreeNode>(treeViewTab4DirectoryTree.Nodes);

If you do decide to stick with your while loop, you can save an instatiation by changing

TreeNode Temp = new TreeNode();

to

TreeNode Temp = null;

... you're never actually using the object you create, at least in the part of the code you've shown.

Arne Sostack
  • 258
  • 2
  • 7
  • @Arne I tried to create a list as you said, but received an error that the constructor was getting an improper parameter. I checked it over and it is getting exactly what it wants, but I can't get rid of the error message. – Christopher B. Adkins Feb 24 '10 at 14:55
  • @Arne also the loop that you suggested only returns the first child of each of the root nodes. That was why I was removing elements. When I was completely looped through one of them I removed it. When there were no more to remove then I was finished. – Christopher B. Adkins Feb 24 '10 at 15:06
  • @Arne Standard WinForms TreeNodeCollection is not IEnumerable : for discussion on making it IEnumerable compatible and sample code for iterating using Linq check out : http://stackoverflow.com/questions/1815497/enumerating-collections-that-are-not-inherently-ienumerable – BillW Feb 24 '10 at 15:38
  • 1
    @Adkins - Ah yes, I missed a detail - you're actually removing every node in the tree from the collection of root nodes, that's what threw me off. I think I'd prefer a stack based solution then (or recursion), mostly because the next guy to read the code will have an easier time figuring out what it does. Will edit to show it done with a stack. – Arne Sostack Feb 25 '10 at 07:28
  • @BillW - Actually, it *is* IEnumerable, just not IEnumerable – Arne Sostack Feb 25 '10 at 08:13
  • @Arne +1 Stack based update is great; if based on "mrydengren's" answer in the SO link I referred you to, please do give him some credit, and might be good to add a link to the Wes Dyer article mentioned there as cited by Eric Lippert. You are right : standard WinForms TreeView Nodes collection is IEnumerable, and IEnumerable is not : which means I need to go back and see why I had such trouble using Linq with the TreeNodes collection in the first place. Thanks ! – BillW Feb 27 '10 at 06:46
  • @BillW - Thanks ;) I didn't actually get around to checking the link. The stack based approach was something I had just recently thought up after a discussion about recursion and how it was useful to know but mostly in order to know how to avoid it. – Arne Sostack Mar 01 '10 at 09:10