Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Count all nodes in a treeview

6 views
Skip to first unread message

John Rogers

unread,
Dec 28, 2007, 5:01:24 PM12/28/07
to
This code only counts the parent nodes or rootnodes in a treeview,
how do you count all the nodes in a treeview?

// one way
int NodeCounter = 0;
foreach (TreeNode currentNode in TreeView1.Nodes)
NodeCounter++;

// other way
int total = TreeView1.Nodes.Count;


Pedro Luna Montalvo

unread,
Dec 28, 2007, 6:12:41 PM12/28/07
to
What about...

public int GetTotalNodes(TreeView treeView)
{
return this.GetTotalNodes(treeView.Nodes);
}

private int GetTotalNodes(TreeNodeCollection nodes)
{
int rootNodes = nodes.Count;

foreach (TreeNode node in nodes)
{
rootNodes += this.GetTotalNodes(node.Nodes);
}

return rootNodes ;
}

Greetings,

"John Rogers" <johnrog...@aol.com> escribió en el mensaje de
noticias:eWEvw0ZS...@TK2MSFTNGP02.phx.gbl...

chri...@gmail.com

unread,
Dec 28, 2007, 6:57:51 PM12/28/07
to
>                 private int GetTotalNodes(TreeNodeCollection nodes)
>                 {
>                         int rootNodes = nodes.Count;
>
>                         foreach (TreeNode node in nodes)
>                         {
>                                 rootNodes += this.GetTotalNodes(node.Nodes);
>                         }
>
>                         return rootNodes ;
>                 }
>

so, count all nodes not just tree nodes... I dont get it... *as
always*

//CY

John Rogers

unread,
Dec 28, 2007, 7:35:36 PM12/28/07
to
Thanks, that worked great.

"Pedro Luna Montalvo" <pete...@hotmail.com> wrote in message
news:3C91FF7A-5E9E-4ADA...@microsoft.com...
> What about...


Peter Duniho

unread,
Dec 28, 2007, 7:56:00 PM12/28/07
to
On Fri, 28 Dec 2007 15:57:51 -0800, <chri...@gmail.com> wrote:

> so, count all nodes not just tree nodes... I dont get it... *as
> always*

What's not to get? The only nodes in a TreeView are going to be
TreeNodes. The statement "count all nodes not just tree nodes" doesn't
make any sense. "All nodes" is the same as "all tree nodes".

Pete

John Rogers

unread,
Dec 28, 2007, 8:21:26 PM12/28/07
to
I thik I jumped the gun here. When I saw the actual number of nodes
it made me think that this is it, unfortunately its not.

Let me see if I can explain clearly what I am trying to do. I am trying
to traverse the entire tree to do the following.

1. Store the Node Text to an ini file
2. Store the Node Level to an ini file
3. Store the Node ImageIndex to an ini file.

I have code that I have used in C++Builder which is very close to C#.
But I still have to do a bit of translating to get everything to copile and
work correctly. This is the code I used in C++Builder and it works great.

//-------------------------------------------------------
TTreeNodes *Nodes = TreeView1->Items;
int val = Nodes->Count;

Ini->EraseSection("TreeNodes");

for( int i = 1; i < val; i++ )
{
Ini->WriteString("TreeNodes", IntToStr(i) + "NodeText",
Nodes->Item[i]->Text);
Ini->WriteInteger("TreeNodes", IntToStr(i) + "NodeLevel",
Nodes->Item[i]->Level);
Ini->WriteInteger("TreeNodes", IntToStr(i) + "NodeIcon",
Nodes->Item[i]->ImageIndex);
if((Nodes->Item[i]->HasChildren) && (Nodes->Item[i]->Expanded))
Ini->WriteBool("TreeNodes", IntToStr(i) + "Expand",
Nodes->Item[i]->Expanded);
}
//------------------------------------------------------------
First I start with the Parent Node as I go down the tree, then if there is a
child I traverse the childnode
and get all of it's settings too. Now having to do this in C# is a bit of a
challenge for me since it's a new
language for me.

I tried this code like this to see if I could actually traverse every node,
but it always falls short.

TreeNodeCollection nodes = tvMain.Nodes;
int rootNodes = nodes.Count;

foreach (TreeNode node in nodes)
{

rootNodes ++;
listBox.Items.Add(node.Text); // this shows 10 nodes
}
MessageBox.Show(Convert.ToString(rootNodes)); // this shows 20
nodes

Instead of the twenty nodes that I have in the tree, I only see ten.
Somehow I am not understanding how
to traverse down through the children nodes too. I am trying to keep the
code very simple so I can understand
it.


Thanks everyone

John


Peter Duniho

unread,
Dec 28, 2007, 9:01:33 PM12/28/07
to
On Fri, 28 Dec 2007 17:21:26 -0800, John Rogers <johnrog...@aol.com>
wrote:

> I thik I jumped the gun here. When I saw the actual number of nodes
> it made me think that this is it, unfortunately its not.

Actually, I think the code you got was exactly what you asked for, and
given the expanded nature of your question is similar to the code you will
eventually need to use.

> Let me see if I can explain clearly what I am trying to do. I am trying
> to traverse the entire tree to do the following.
>
> 1. Store the Node Text to an ini file
> 2. Store the Node Level to an ini file
> 3. Store the Node ImageIndex to an ini file.
>
> I have code that I have used in C++Builder which is very close to C#.
> But I still have to do a bit of translating to get everything to copile
> and
> work correctly. This is the code I used in C++Builder and it works
> great.
>
> //-------------------------------------------------------
> TTreeNodes *Nodes = TreeView1->Items;
> int val = Nodes->Count;

What type is "TreeView1"? Where does the TTreeNodes type come from?

The code you posted doesn't have any sort of recursion or "generation"
traversal of any sort that I see. Which suggests that the TTreeNodes type
exposes the entire tree as a flat hierarchy via its "Item" member. It's
hard to say for sure without knowing what that type is, but it's one
possible conclusion based on the code you posted.

So, with that in mind it's important for you to understand that the
TreeNodeCollection you get in .NET behaves differently. Its "Nodes"
member is a collection _only_ of the direct descendants of the item from
which you got the collection. You have to explicitly enumerate the
"Nodes" member of each of those descendants, and so on, in order to get
the entire tree.

Pedro's code does this via recursion, returning for each node the sum of
the number of direct descendants and the calculated value of nodes from
each of those descendants.

Now, you've added the requirement to be able to emit text information
based on each node. You can still do that, simply by including that code
in the basic function Pedro posted. Each node would be handled within the
foreach() loop, writing out whatever text is required.

> [...]


> First I start with the Parent Node as I go down the tree, then if there
> is a
> child I traverse the childnode and get all of it's settings too.

I don't see anything in the code you posted that treats child traversal
differently. Did you leave something out? That's another possible
conclusion based on the code you posted.

> Now having to do this in C# is a bit of a
> challenge for me since it's a new language for me.
>
> I tried this code like this to see if I could actually traverse every
> node,
> but it always falls short.
>
> TreeNodeCollection nodes = tvMain.Nodes;
> int rootNodes = nodes.Count;
>
> foreach (TreeNode node in nodes)
> {
> rootNodes ++;
> listBox.Items.Add(node.Text); // this shows 10 nodes
> }
> MessageBox.Show(Convert.ToString(rootNodes)); // this shows
> 20
> nodes

I don't understand that code at all. It simply counts the top-level nodes
twice. If you had a tree that had 15 total nodes, with 10 top-level
nodes, you'd still get a count of 20. Nothing about that code follows the
pattern provided by Pedro's example.

> Instead of the twenty nodes that I have in the tree, I only see ten.

In that C# code example, the value displayed in the message box will
always be twice the actual number of top-level nodes. Simple as that. If
you're testing the code on a tree that has 20 total nodes in it, then the
coincidence is just misleading you into thinking that the code correctly
traverses the tree when counting. It doesn't do that at all.

> Somehow I am not understanding how
> to traverse down through the children nodes too. I am trying to keep the
> code very simple so I can understand it.

Well, to some extent recursion is very simple. In another sense, it's
very complicated, if you're not familiar with the concept. To deal with a
recursive structure like a TreeView in .NET, you will need to understand
recursion. So if you don't understand recursion, it's easy to see how
this might seem confusing.

If you don't understand recursion, you might think about reading up on
that first, before attempting to implement something that requires
recursion.

If you do understand recursion, maybe you could be more specific about why
you're trying to enumerate a recursive data structure without writing any
recursive code. The C# code example you posted certainly doesn't come
close to being what would produce correct results, and that's actually
true even if your tree had only top-level nodes.

Pete

John Rogers

unread,
Dec 28, 2007, 10:08:26 PM12/28/07
to

>What type is "TreeView1"? Where does the TTreeNodes type come from?

TreeView1 is a Treeview, TTreeNodes is Nodes from the tree.

The code that I posted from C++Builder recurses an entire tree and you can
get whatever properties you want. I would much have desired to stay with
C++Builder rather than going to learn a new language. But since there is
no support for pocket pc, I have no choice but to learn C#. Since what I
am writing is a program that will work on th desktop and on the Pocket PC.

I stripped down Pedro's code a bit so I could get the understanding of the
recursion. This foreach() stuff is confusing, since I don't have a number
from
the loop like I though I would.

Anyway, let me try it again and see if i can get the properties I need.
Because
after I save the settings, I still have to load them back into the Tree.


John


John Rogers

unread,
Dec 28, 2007, 10:38:04 PM12/28/07
to
I have to admit that I just don't get this.

// this is what I am adding my nodes to the tree with
tvMain.BeginUpdate();
tvMain.Nodes.Clear();
for (int i = 0; i < 10; ++i)
{
TreeNode ParentNode = tvMain.Nodes.Add("Parent Node: " +
Convert.ToString(i));
ParentNode.Nodes.Add("Child Node: " + Convert.ToString(i));
}
tvMain.EndUpdate();


// this is what i am checking the nodes with
GetTotalNodes(TreeView1);


public int GetTotalNodes(TreeView treeView)
{
return this.GetTotalNodes(treeView.Nodes);
}
private int GetTotalNodes(TreeNodeCollection nodes)
{

int rootNodes = nodes.Count;

foreach (TreeNode node in nodes)
{

//listBox.Items.Add(node.Text + " " +
Convert.ToString(Counter));
listBox.Items.Add(node.Text);
rootNodes += this.GetTotalNodes(node.Nodes);
Counter++;

}
return rootNodes;
}


This is what I am looking for. If I have 20 nodes, when I iterate the tree,
I am looking
for numbers from 1-20 telling me there are 20 nodes. If I have 3000 nodes,
I want the
numbers to go from 1-3000 telling me there are 3000 nodes on that tree.

This foreach() isn't giving me any numbers like I would get if I do a loop
like
for(int x = 0; x < treeview.nodes.count; ++x)

At least with that code I get a numeric value. That is important if I am
going to store the
tree layout and restore it again. That is unless someone is willing to show
me how to
store and load a treeview layout, because right now I have absolutely no
clue.


Thanks


Peter Duniho

unread,
Dec 29, 2007, 12:28:03 AM12/29/07
to
On Fri, 28 Dec 2007 19:38:04 -0800, John Rogers <johnrog...@aol.com>
wrote:

> [...]


> This is what I am looking for. If I have 20 nodes, when I iterate the
> tree,
> I am looking
> for numbers from 1-20 telling me there are 20 nodes. If I have 3000
> nodes,
> I want the
> numbers to go from 1-3000 telling me there are 3000 nodes on that tree.

Well, again...that wasn't part of your original question. If I understand
the above statement, you want to iterate the nodes in some specific
order. Do you literally need a counter that is assigned to each node in
order? Or are you simply looking for a specific order of enumeration?

The nodes you originally create, if I read the code right, actually
include two nodes with the same number: the parent and its single child.
So your demo tree looks like this:

Parent Node: 0
Child Node: 0
Parent Node: 1
Child Node: 1
Parent Node: 2
Child Node: 2
.
.
.
Parent Node: 9
Child Node: 9

Right?

So given that, the order of numbers isn't 1-20, it's actually 0 through 9
with each number appearing twice (0, 0, 1, 1...9, 9).

> This foreach() isn't giving me any numbers like I would get if I do a
> loop
> like
> for(int x = 0; x < treeview.nodes.count; ++x)
>
> At least with that code I get a numeric value. That is important if I am
> going to store the
> tree layout and restore it again. That is unless someone is willing to
> show
> me how to
> store and load a treeview layout, because right now I have absolutely no
> clue.

Well, there seem to be inconsistencies in your description of what you're
trying to do. That alone makes it difficult to help.

That said, given the basic task of enumerating a tree to save it and then
restoring it later, it's not that hard. But there is one very important
rule: you must enumerate the nodes in identical order when reading the
tree as when writing it, or you must store relational information (i.e.
unique IDs for each node, and with each node the unique ID of its parent)
so that the order of recreation doesn't matter.

It sounds to me as though one big problem you're having is that the tree
traversal is done differently when you enumerate it than when you recreate
it. Certainly the code you posted has that problem.

I don't see anything in the code C++Builder code you posted that would
help me understand how you're currently reconstituting the tree based on
the data you write out. Assuming you haven't left anything out of the
code that writes the nodes out, the reconstitution code must assume a
specific order for the nodes. That combined with the node level (which I
assume is the depth in the tree?) is enough to regenerate the original
tree from that information. But even so, the same requirement exists in
that situation as for what I'm describing above: you have to do the
traversal in the same order coming back in as you did when writing the
data out.

You could of course do something similar here, tracking not only the node
count but also the depth within the tree. Then you could use the depth
information to know when you were done handling children for a given
node. For example:

struct NodeInfo
{
public readonly string name;
public readonly int depth;

public NodeInfo(string name, int depth)
{
this.name = name;
this.depth = depth;
}
}

List<NodeInfo> SavedTree = new List<NodeInfo>();

void SaveTree(TreeView tv)
{
SaveTree(tv.Nodes, 0);
}

void SaveTree(TreeNodeCollection nodes, int depth)
{


foreach (TreeNode node in nodes)
{

SavedTree.Add(new NodeInfo(node.Text, depth));
SaveTree(node.Nodes, depth + 1);
}
}

void RestoreTree(TreeView tv)
{
RestoreTree(tv.Nodes, 0, 0);
}

int RestoreTree(TreeNodeCollection nodes, int depth, int inode)
{
TreeNode nodePrev = null;

while (inode < SavedTree.Count && SavedTree[inode].depth >= depth)
{
if (SavedTree[inode].depth == depth)
{
nodePrev = nodes.Add(SavedTree[inode++].name);
}
else
{
inode = RestoreTree(nodePrev.Nodes, depth + 1, inode);
}
}

return inode;
}

I used a List<> to store the data, and of course the data I saved doesn't
include any properties other than the name and depth within the tree (it
appears you have additional things you'd want to save). But the above is
the basic outline for what you want.

Essentially, you create a linear list of all the nodes, storing the depth
of each node in the list along with whatever other data you want. Then,
to restore the tree, you use that depth information to decide whether the
next nodes should be added as siblings, children, or as a sibling to the
parent of the current node.

I think this code is a good example of how recursion is both simple and
complicated at the same time. On the one hand, the code itself is very
brief, and IMHO clear to someone who understands recursion. On the other
hand, there are some self-referential things going on in the code that
even in the few short lines could be non-intuitive to someone not familiar
with recursion. Hopefully you're familiar enough with the concepts that
the code makes sense to you, and if not I'll reiterate my recommendation
that you should learn a little more about recursion before going much
further with this particular problem.

As near as I can tell from what you've posted, C++Builder has hidden some
(most? all?) of the recursive nature of your data structure from you,
handling that all behind the scenes. Which I suppose is fine for a
specific purpose, but it seems to have put you at a disadvantage with
respect to trying to translate the code into an environment where the
recursion is more explicit.

Hopefully this is enough information to get you back on track.

Pete

John Rogers

unread,
Dec 29, 2007, 11:26:29 AM12/29/07
to
Hi Pete,

>the above statement, you want to iterate the nodes in some specific
>order. Do you literally need a counter that is assigned to each node
>in order? Or are you simply looking for a specific order of
>enumeration?

When I wrote my function to save and load the tree, I wrote it using
the
ItemIndex of the nodes in the tree. In CBuilder the nodes will go from
1-?? however many you have in the tree when doing the recursion.
Even if the nodes are nested 10 deep the numbers will still go in
order.

// in c# the numbers will be like this as you pointed out


Parent Node: 0
Child Node: 0
Parent Node: 1
Child Node: 1
Parent Node: 2
Child Node: 2

// in cbuilder the numbers will be like this
Parent Node: 1
Child Node: 2
Parent Node: 3
Child Node: 4
Parent Node: 5
Child Node: 6


>I don't see anything in the code C++Builder code you posted that
>would help me understand how you're currently reconstituting the
>tree based on

Not yet, I was just posting the storing of the tree to get it to work.
Rebuilding
the tree would be a lot more difficult. I didn't attempt that one
yet.

>As near as I can tell from what you've posted, C++Builder has hidden
>some (most? all?) of the recursive nature of your data structure
>from you, handling that all behind the scenes. Which I suppose is
>fine for a specific purpose, but it seems to have put you at a
>disadvantage with

Absolutely right, C++Builder keeps a lot of items behind the scenes so
you
can write code faster. As a matter of fact, I just found out the
other day from
a friend who codes in Delphi and has been using C# for quite a while.
He told
me that the same person who wrote Delphi wrote C#. I kinda wondered
how come
the syntax is so close, and the IDE looks almost the same in Delphi,
C++Builder, and
C#.

C# also hides a lot of the behind the scenes options when using the
extensive library
called the toolbox. This makes developing a lot easier and faster for
the developer.

Thanks again for you help Pete.


John


John Rogers

unread,
Dec 29, 2007, 9:49:38 PM12/29/07
to
Peter,

I have one more question. I have been reading and searching quite a
bit
to find the answer, but I have not had any luck since I don't know

what I
am looking for.

struct NodeInfo


{
public readonly string name;
public readonly int depth;

public NodeInfo(string name, int depth)
{
this.name = name;
this.depth = depth;
}
}

After you create your struct and pass the values to the struct, how do
you access
the values? I was assuming that it would be like this

// create my list


List<NodeInfo> SavedTree = new List<NodeInfo>();

// add information to the list
SavedTree.Add(new NodeInfo(node.Text, depth));

// access the info like this, but this does not work.
listBox.Add(SavedTree.name);

This is strange, thats all I have to say.


John

Peter Duniho

unread,
Dec 29, 2007, 10:39:18 PM12/29/07
to
On Sat, 29 Dec 2007 18:49:38 -0800, John Rogers <johnrog...@aol.com>
wrote:

> [...]


> After you create your struct and pass the values to the struct, how do
> you access
> the values?

By accessing an instance of the struct and referencing the public member
fields.

> I was assuming that it would be like this
>
> // create my list
> List<NodeInfo> SavedTree = new List<NodeInfo>();

This creates a List that can contain instances of NodeInfo. It's sort of
like initializing an array like this:

NodeInfo[] array = new NodeInfo[10];

except that you don't need to know how many elements you want in advance.

> // add information to the list
> SavedTree.Add(new NodeInfo(node.Text, depth));

This initializes an instance of NodeInfo and pass that instance to the
List.Add() method. At this point the List now contains a copy of that
instance of NodeInfo. It's sort of like assigning a value to an array
element:

array[5] = new NodeInfo(node.Text, depth);

except that you don't have to keep track of where the current place to add
an element in the array is, and if the data structure becomes filled, it
automatically reallocates itself internally when you try to add more.

> // access the info like this, but this does not work.
> listBox.Add(SavedTree.name);

The SavedTree variable is the list itself. It doesn't have a "name"
property or field.

The semantics of the List<> class are similar in many respects to arrays.
Just as you have to index an element in an array to get at the values
stored there, you have to index an element in the List<> to get at the
values stored there. The List<> itself doesn't know anything about those
values any more than an array would.

The code I posted shows how you _do_ get at the information in the
NodeInfo instances. See the RestoreTree() method for examples.

> This is strange, thats all I have to say.

I don't know why it's strange. Either you're not getting enough sleep, or
you need to get back to basics and review the basic .NET data structures
(or maybe even C arrays). :)

Pete

John Rogers

unread,
Dec 29, 2007, 11:21:08 PM12/29/07
to
>I don't know why it's strange. Either you're not getting enough
>sleep, or you need to get back to basics and review the basic .NET
>data structures

Believe me this is so different from what I am used to, I am reading a
few books
dealing with C#. But I like books with real world examples and I
don't see
many examples out there. Plus I haven't done any coding in about two
years.

Thanks again Pete


John


0 new messages