Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / January 2008

Tip: Looking for answers? Try searching our database.

Unique identifier in every treenode?

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Rob Stevens - 03 Jan 2008 19:40 GMT
Is there some sort of unique identifier in every treenode
that is consistent?   I was looking at the handle of every
treenode, but it appears that the handle changes everytime
the tree is built.

I have seen in some databases where they store the treenodes
they are using some serial number of the node.  I don't have
a clue what that is, but can someone tell me if there is something
that is unique to every node.

Thanks

Rob
Nicholas Paldino [.NET/C# MVP] - 03 Jan 2008 19:56 GMT
Rob,

   Well, within the confines of the program itself, then the reference
itself is unique, and you should be able to compare object references.

   If you are talking about something you can persist, then you need to
look at other options.  You need to associate that identifier with the node.

   If the full path consisting of the label names represent a unique
identifier, then you can use the FullPath property on the TreeNode instance
to get the path from the root to that node.  If not, then you will have to
assign something to the Tag property, creating the identifier and restoring
it when you recreate the tree from whatever underlying storage you are
using.

Signature

         - Nicholas Paldino [.NET/C# MVP]
         - mvp@spam.guard.caspershouse.com

> Is there some sort of unique identifier in every treenode
> that is consistent?   I was looking at the handle of every
[quoted text clipped - 9 lines]
>
> Rob
Rob Stevens - 03 Jan 2008 20:04 GMT
Thanks Nicholas,

I ask this question because I have been looking at a lot of example code
within the
last week.  And it's really confusing the way the tree is being stored to
whatever
storage is being used, and restored with the same exact layout still remains
a
mystery to me.  I see no where in anyone's code where they are storing node
names,
levels, nothing.  Here is an example I found last night, but even after
debugging and
looking at the values, I still can't see anything at all that shows me the
exact layout of
a tree.

       void SaveTreeView(TreeView tree, string filename)
       {
           ArrayList al = new ArrayList();
           foreach (TreeNode tn in tree.Nodes)
           {
               al.Add(node);
               //listBox.Items.Add(node.Text + "," + depth);
           }

           Stream file = File.Open(filename, FileMode.Create);
           BinaryFormatter bf = new BinaryFormatter();
           try
           {
               bf.Serialize(file, al);
           }
           catch (System.Runtime.Serialization.SerializationException e)
           {
               MessageBox.Show("Serialization failed : {0}", e.Message);
           }
           file.Close();
       }

       void LoadTreeView(TreeView tree, string filename)
       {
           if (File.Exists(filename))
           {
               Stream file = File.Open(filename, FileMode.Open);
               BinaryFormatter bf = new BinaryFormatter();
               object obj = null;
               try
               {
                   obj = bf.Deserialize(file);
               }
               catch (System.Runtime.Serialization.SerializationException
e)
               {
                   MessageBox.Show("De-Serialization failed : {0}",
e.Message);
               }
               file.Close();

               ArrayList nodeList = obj as ArrayList;

               // load Root-Nodes
               foreach (TreeNode node in nodeList)
               {
                   tree.Nodes.Add(node);
                   //listBox.Items.Add(node.Text);
               }
           }
       }

I think until I figure out a lot of these underlying things in the code, I
will never understand what
I am looking at. I can't see individual properties being saved, its just a
mystery.

Rob

> Rob,
>
>    If you are talking about something you can persist, then you need to
> look at other options.  You need to associate that identifier with the
> node.
Peter Duniho - 03 Jan 2008 20:32 GMT
> I ask this question because I have been looking at a lot of example code  
> within the
[quoted text clipped - 9 lines]
> the
> exact layout of a tree.

That's because all the important stuff is hidden in the serialization code  
for TreeNode.

I'm not really all that familiar with the specifics myself, but the code  
you posted simply enumerates the root nodes, serializing each one.  Then  
in the "Load" method, it deserializes the stored data into an array of  
TreeNodes, representing the root nodes, and adds each root node to the  
tree.

Since TreeNode implements ISerializable and assuming the code you posted  
works, it must include logic for serializing the entire structure under a  
given TreeNode.  To see specifically how it works, you would have to look  
at the implementation for TreeNode itself, and/or the output from the  
serializer (but you'd probably want to use a different formatter than  
BinaryFormatter, so that you get something that's reasonable  
human-readable).  But there's nothing magical about it...it just means  
that the code you posted is relying on an existing implementation that  
already handles the job of relating the nodes somehow.

You could either take advantage of that, or write your own  
implementation.  Depending on your needs with respect to persisting the  
data, either approach could be warranted.

Pete
Rob Stevens - 03 Jan 2008 21:29 GMT
> You could either take advantage of that, or write your own
> implementation.  Depending on your needs with respect to persisting the
> data, either approach could be warranted.

I found this one today that I had seen before but did not really pay any
attention
to it.

       private void button1_Click(object sender, EventArgs e)
       {
           foreach (TreeNode node in tvMain.Nodes)
           {
               PrintNodeInformation(node);
           }
       }
       public void PrintNodeInformation(TreeNode node)
       {
           listBox.Items.Add(node.Text + "," + node.Level);
           //Console.WriteLine("Text: " + node.Text);
           //Console.WriteLine("Path: " + node.FullPath);
           //Console.WriteLine("Tag: " + node.Tag);

           foreach (TreeNode childNode in node.Nodes)
           {
               PrintNodeInformation(childNode);
           }
       }

This code seems really simple and straight to the point, it does pick up
every node in the
tree, this is a good start for what I want to do.  I guess I will have to do
as suggested,
save the tree in order, and restore it in the same order.  I don't know how
I will be
able to use the tag information for this, I am thinking about storing data
attached to
each node when I attach to a db.  And as far as I understand the tag
property is normally
used for storing your structs, that is as far as I have read from many other
posts.

Rob
Peter Duniho - 03 Jan 2008 22:14 GMT
> This code seems really simple and straight to the point, it does pick up
> every node in the tree, this is a good start for what I want to do.

The important part about that code is the recursion, which will be a  
common element in any implementation that enumerates the tree completely.

> I guess I will have to do as suggested,
> save the tree in order, and restore it in the same order.

That is one way, yes.

> I don't know how I will be
> able to use the tag information for this,

You wouldn't.  If you are taking advantage of the Tag property, it won't  
matter what order the data is stored (though you'll probably still wind up  
with a reliably reproducible order...it would be silly to intentionally  
randomize the order of the data as stored :) ).

As an example, you could create a new Guid for each TreeNode and assign it  
to the Tag property.  Then when saving, you'd include for each node the  
Guid of the _parent_ of that node (stored in the Tag property of the  
parent, of course).  Then reconstructing the tree is as simple as looking  
at the parent Guid stored with each node's data (not that node's own Guid,  
in the Tag property), and using that to find the parent for the node.

> I am thinking about storing data attached to each node when I attach to  
> a db.

I'm no database expert.  But my understanding is that you can create a  
database where one of the columns is a unique identifier, and optionally  
where the unique identifier is automatically generated when you add a  
record to the database.

In this case, instead of creating a new unique identifier in the context  
of your TreeView with each TreeNode, you could wait until you've written a  
given node to the database to find out what the unique identifier in the  
database is for that node, and then store that identifier for each child  
of the node as part of the child node's database record (root nodes would,  
of course, have to have some sort of null value).

I don't know how the specific database code would look, but in pseudocode  
the basic idea would be something like this:

    void AddNodesToDatabase(TreeNodeCollection nodes, int DBParentID)
    {
        foreach (TreeNode node in nodes)
        {
            int DBNodeID = AddNodeToDatabase(node, DBParentID);

            AddNodesToDatabase(node.Nodes, DBNodeID);
        }
    }

The assumption being that the method "AddNodeToDatabase" will save the  
node data along with the parents database ID as a single record in the  
database, and then return the database's unique identifier for that  
newly-added node.  The ID can then be used to add the children nodes for  
that node.

In this way, the tree structure is represented in the database.  Of  
course, you still need to reconstitute the tree from that information  
later.  That will be a matter of tracking, at least temporarily, the  
database ID for each node as they are read out.  For example (again,  
pseudo-code):

    Dictionary<int, TreeNode> NodeLookup = new Dictionary<int, TreeNode>();

    void GetNodesFromDatabase(TreeNodeCollection nodes)
    {
        // Obviously we need to enumerate each record in the database
        foreach (TreeNodeRecord record in Database)
        {
            // These three lines represent the basic functionality you'll  
need to figure out
            // with respect to the database.  From the database record,  
you need to be able
            // to construct a node, retrieve the ID for that node, as well  
as the ID for the
            // parent node.
            TreeNode node = TreeNodeFromRecord(record);
            int DBParentID = ParentIDFromRecord(record);
            int DBNodeID = NodeIDFromRecord(record);

            if (DBParentID != DBNullID)
            {
                // If the parent node is in your dictionary, add the new
                // node as a child of the parent node
                NodeLookup[DBParentID].Nodes.Add(node);
            }
            else
            {
                // Otherwise, add it as a root note
                nodes.Add(node);
            }

            // Store the new node in the lookup dictionary, so that its  
children
            // can be added
            NodeLookup.Add(DBNodeID, node);
        }
    }

The above code assumes that parents are always written before their  
children.  It's not a requirement that is strictly necessary, but it makes  
the code simpler and more efficient and is easy enough to implement (it  
comes naturally from the previous code example).

> And as far as I understand the tag property is normally
> used for storing your structs, that is as far as I have read from many  
> other posts.

The Tag property can be used for attaching any user-defined data you like  
to the TreeNode.  That could be run-time specific data, or some kind of  
unique identifier for the node, or whatever.  It's up to the application  
to decide how to use the Tag property.

The previous thread I mentioned, I wrote a simple demo of saving and  
restoring a TreeView.  The code is all in-memory...it doesn't save  
anything to persistent storage.  But the basic ideas are going to be the  
same.  The post in which I included that sample is here:
http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/msg/018a
8db8e8a6033e


You may find that post, or even the entire thread, useful.  Or not.  You  
might as well look to see though.  :)

Pete
Rob Stevens - 03 Jan 2008 22:34 GMT
Thanks Pete,

You have given me a lot of great ideas, now I have to put them to use.

Thanks again.

Rob
Peter Duniho - 03 Jan 2008 20:05 GMT
> Is there some sort of unique identifier in every treenode
> that is consistent?   I was looking at the handle of every
[quoted text clipped - 5 lines]
> a clue what that is, but can someone tell me if there is something
> that is unique to every node.

Not by default, no.  It would be up to the client code (i.e. what you  
write) to impose any sort of unique identifier and store it with the  
TreeNode (you can either use the TreeNode.Tag property to store something,  
or subclass the TreeNode class and put whatever you want in that derived  
class).

You may or may not actually need to store a unique identifier.  Even in a  
regular database you can use the database itself to specify relationships  
between records in the database representing nodes, something like XML  
provides a natural "containment" semantic that would allow the tree  
structure to be represented without explicit references between nodes, and  
even a plain text file can be used to reliably store a tree structure as  
long as you have _some_ way of describing the structure (for example,  
instead of specifying explicit relationships between nodes, simply storing  
a given node's depth within the tree along with making sure the nodes are  
stored and recovered in a specific order is sufficient...this was even a  
recent example in a previous thread in this newsgroup).

Pete

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.