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.

Last try at this treeview

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
John Rogers - 07 Jan 2008 01:29 GMT
I had given up on this for a while, but I am attempting it again.

This is what i am using to store my treeview layout into a db, I
am only using a few fields to make it easier to understand while
I am attempting to restore it back into the tree.

Text "Parent0", Level 0
Text "Child0", Level 1
Text "SubChild0", Level 2

Now I have read many posts where others are saying to store a unique
id
of the parent.  I am having a bit of trouble trying to store an id
somewhere
and retrieve it on the next loop.

This is what I am looking at in the debugger, I am looking for
something that
I can use that is unique. This is a multilayer, many roots and many
nested nodes
in the treeview.  This is just a sample I am posting.

//-------------------- Debugger values ------------------------------
+  Parent null System.Windows.Forms.TreeNode
 Text "Parent0" string
 Level 0 int
+  FirstNode {Text = "Child0"} System.Windows.Forms.TreeNode
+  NextNode {Text = "Parent1"} System.Windows.Forms.TreeNode
+  NextVisibleNode {Text = "Child0"} System.Windows.Forms.TreeNode

+  Parent {Text = "Parent0"} System.Windows.Forms.TreeNode
 Text "Child0" string
 Level 1 int
+  FirstNode {Text = "SubChild0"} System.Windows.Forms.TreeNode
+  NextNode null System.Windows.Forms.TreeNode
+  NextVisibleNode {Text = "SubChild0"} System.Windows.Forms.TreeNode

+  Parent {Text = "Child0"} System.Windows.Forms.TreeNode
 Text "SubChild0" string
 Level 2 int
+  FirstNode {Text = "SubSubChild0"} System.Windows.Forms.TreeNode
+  NextNode null System.Windows.Forms.TreeNode
+  NextVisibleNode {Text = "SubSubChild0"}
System.Windows.Forms.TreeNode

//-------------------- Debugger values ------------------------------

Lets say I start the loop, my first node will be "Parent0" it does not
have a parent, its a
rootnode, the ParentID for this will be NULL.  On th next round I come
up with "Child0"
and that node has a parent which is "Parent0".  Now how would I
generate a GUID for
Parent0, and tell Child0 who its parent is?  Even if I create an
instance of my struct and store
the information in the struct.  That information will be lost on my
next round of the loop because
I have to create a new instance of the struct again.

I am pretty confused right now, maybe I am over analyzing everything
and thats why i cant
get anything to work.  Here is some sample code that leads to nowhere.

           SaveTreeViewRecords SaveRecords = new
SaveTreeViewRecords();
           foreach (TreeNode node in TreeView1.Nodes)
           {
               SaveRecords.SaveDBRecords(node);
           }

   class SaveTreeViewRecords
   {
       public void SaveDBRecords(TreeNode node)
       {
           // create dataset connections here

           String parentID = Guid.NewGuid().ToString();
           String nodeText = node.Text;
           int nodeLevel = node.Level;

           // store information to db here

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

My class is longer than that, but I just want to keep this simple
since I am confused already.

This is what my sample tree look like until I can get this thing
working.

           for (int x = 0; x < 2; ++x)
           {
               TreeNode ParentNode = tvMain.Nodes.Add("Parent" +
x.ToString());
               TreeNode ChildNode = ParentNode.Nodes.Add("Child" +
x.ToString());
               TreeNode SubChildNode = ChildNode.Nodes.Add("SubChild"
+ x.ToString());
               TreeNode SubSubChildNode =
SubChildNode.Nodes.Add("SubSubChild" + x.ToString());
           }

Once I can get the storing of the information to the DB, then I can
worry about loading it back
in.  But thats another headache I am not eady to tackle just yet.

Thanks

John
Peter Duniho - 07 Jan 2008 02:42 GMT
> [...]
> Lets say I start the loop, my first node will be "Parent0" it does not
[quoted text clipped - 4 lines]
> generate a GUID for
> Parent0, and tell Child0 who its parent is?

You don't need to tell Child0 who its parent is, but even if you did it's  
as easy as doing so in the loop where you enumerate the children of the  
parent.

In your own example, I think the main thing you need to do is add a  
parameter to the SaveDBRecords() method that takes the Guid you created.  
Pass null for the parameter at the top level, and then pass the parent  
Guid when looping for each child.  The parent Guid would be written along  
with the rest of each node's information (handling null as appropriate to  
your database, of course).  Of course, the node's information should  
include its own Guid as well, so for each node record you'll be writing  
two Guids.

I should say: you don't seem all that familiar with recursion, and you've  
picked a somewhat challenging task with which to introduce yourself to  
recursion.  You might want to review some more basic examples and try to  
become more comfortable with recursive algorithms before trying to get  
this one done.  It might make your life easier.

Pete
John Rogers - 07 Jan 2008 03:15 GMT
Thanks for the tip Pete.

> I should say: you don't seem all that familiar with recursion, and
> you've  picked a somewhat challenging task with which to introduce
> yourself to

Actually I hate recursion. Even when I coded in c++builder I always
had problems with
recursion.  Just as you are helping me here, it was the same there,
others helped me out
until I got the hang of it.

But really, the treeview is really the only control that I have
problems with.  It takes a lot
of though on how to handle the recursion.  I actually do have it
working, its just a matter
of writing a guid for each record which is not a problem.  But i was
thinking too technical
and that causes me more problems than I would normally have.

I will implement what you said and see how much further I can get with
the tree structure.

John
bob - 07 Jan 2008 04:14 GMT
>I had given up on this for a while, but I am attempting it again.
>
[quoted text clipped - 5 lines]
>Text "Child0", Level 1
>Text "SubChild0", Level 2

<Stuff Deleted>

Hi John,
How about deriving a class from treenode that has a 'NodeId' and a
'PrevNodeId' property.

Also have a  int that can provide a sequence of Ids during the
creation phase.

Everytime you create a node you assign the next value of the  int to
the NodeId.

I take it your business logic decides who the parent is of any node.
This logic assigns the PrevNodeId field.

Roots are detectable because their PrevNodeId is 0
hth
Bob
John Rogers - 07 Jan 2008 04:53 GMT
> Hi John,
> How about deriving a class from treenode that has a 'NodeId' and a
> 'PrevNodeId' property.

Hi Bob,

Thats exactly what I have.  I thought I had it figured out a little
while ago, but when I
changed the structure of the tree, I realized that I am still lost.
This is my code so far.

Don't laugh, my code may look funny but I code like this to get things
working.  Then when
its working I start fixing up the code to make it more efficient.

I am not using anything in the struct yet, I am just using some global
variables for now.

// layout of my tree
   struct NodeLayout
   {
       public string node_id;
       public string parent_id;
       public string node_text;
       public int node_level;
   }
// this will store some temp variables.
   public class tmpvar
   {
       public static String ParentGUID = "";
       public static String NodeGUID = "";
       public static String NodeParent = "";
   }

   class SaveTreeViewRecords
   {
       public void SaveDBRecords(TreeNode node)
       {
           // create a connection to the db stuff here

           // setup some temp variables to store in the struct
           String nodeText = node.Text;
           int nodeLevel = node.Level;
           int imageIndex = node.ImageIndex;
           bool nodeExpanded = node.IsExpanded;

           if (node.Parent == null)
               tmpvar.NodeParent = "";
           else
               tmpvar.NodeParent = node.Parent.Text;

           // root nodes
           if (node.Level == 0)
           {
               tmpvar.NodeGUID = Guid.NewGuid().ToString();
               tmpvar.ParentGUID = "";
           }
           else // child nodes
           {
               if (node.Parent.Text == tmpvar.NodeParent)
               {
                   tmpvar.ParentGUID = tmpvar.NodeGUID;
                   tmpvar.NodeGUID = Guid.NewGuid().ToString();
               }
           }

           drNewRow["NodeGUID"] = tmpvar.NodeGUID;
           drNewRow["ParentGUID"] = tmpvar.ParentGUID;
           drNewRow["NodeText"] = node.Text;
           drNewRow["NodeLevel"] = node.Level;

    // apply my db changes here

    // loop again
           foreach (TreeNode childNode in node.Nodes)
           {
               SaveDBRecords(childNode);
           }
       }
   }

// I create some temp nodes with this code for testing
           treeView1.BeginUpdate();
           treeView1.Nodes.Clear();
           for (int x = 0; x < 3; ++x)
           {
               TreeNode ParentNode = treeView1.Nodes.Add("Parent" +
x.ToString());
               TreeNode ChildNode = ParentNode.Nodes.Add("Child" +
x.ToString());
               TreeNode SubChildNode = ChildNode.Nodes.Add("SubChild"
+ x.ToString());
               TreeNode TestNode = ParentNode.Nodes.Add("TestChild" +
x.ToString());
               TreeNode SubSubChildNode =
SubChildNode.Nodes.Add("SubSubChild" + x.ToString());
           }
           treeView1.ExpandAll();
           treeView1.EndUpdate();

You see Pete thinks the recursion is killing me, but it's not.  Its
trying to get these unique id's into the db
and being able to tell who belongs to who.  I threw a wrench into the
code when I added in two nodes
with the same parent, that just ruined it for me.

To be honest with you, I really don't care how the tree layout is
stored to the database.  But in order to
store the layout into a db, you need the values of every single node
that you are going to insert into the db.

And when I get ready to restore it from the db back to the tree, my
next problem will be telling the node
where to add the child.  Adding the roots are easy, but this syntax is
throwing me a curve.  If I knew
delphi it would be so much easier to get used to this syntax.

John
Peter Duniho - 07 Jan 2008 05:37 GMT
> [...]
> // layout of my tree
[quoted text clipped - 5 lines]
>         public int node_level;
>     }

Where would you ever use this struct?  It's not required for the basic  
problem description you've offered.

> // this will store some temp variables.
>     public class tmpvar
[quoted text clipped - 3 lines]
>         public static String NodeParent = "";
>     }

Static variables and recursion are not typically things that go with each  
other.  Very rarely, you might need to keep some global state, but the  
point of recursion is to keep the problem isolated in way that treats each  
iteration of recursion individually.  You can't do this with static  
variables, because deeper iterations of the recursion affect the data in  
use later by the shallower iterations.  The only way this would be okay is  
if your recursion is strictly tail recursion, and in that case you  
wouldn't need recursion anyway.

The fact that you state that the recursion isn't part of your difficulty  
is contrary to the code you've posted as well as to your questions.  IMHO,  
as long as you believe that understanding the recursion isn't part of the  
problem, you won't make much progress to really understanding the problem.

And in fact, in the code you posted, these static variables and your  
apparent misunderstanding regarding how they botch up the recursion are in  
fact the entire cause of the problem.  So, looking at that code you  
posted...

>     class SaveTreeViewRecords
>     {
[quoted text clipped - 7 lines]
>             int imageIndex = node.ImageIndex;
>             bool nodeExpanded = node.IsExpanded;

Since you don't use the struct, obviously the above is superfluous.

>             if (node.Parent == null)
>                 tmpvar.NodeParent = "";
>             else
>                 tmpvar.NodeParent = node.Parent.Text;

What's the point of this?  This is the sort of thing I'm talking about: in  
a proper recursive implementation, there would be no need to store any of  
the state of the recursion in a static variable.  And indeed, looking at  
the rest of the implementation...

>             // root nodes
>             if (node.Level == 0)
[quoted text clipped - 10 lines]
>                 }
>             }

What's up with this last if() statement?  Shouldn't tmpvar.NodeParent  
always be the empty string when node.Level is 0, and set to  
node.Parent.Text when it's not?  Given that, shouldn't the condition in  
the if() statement always be true?  And, heaven forbid, you've got a bug  
in which it's not, you've got code that can get all the way to saving the  
node information in a database without having either the parent or node  
Guid initialized.

Because tmpvar.NodeParent is always initialized just before it's checked,  
the fact that it's a static variable should not cause any problems in this  
particular case.  But it also means that it could just as easily be a  
local variable.

Of course, really when you look at the whole thing what it means is that  
you don't need that variable at all.

Of greater concern is the line that assigns "tmpvar.ParentGUID" for  
non-root nodes.  This is where using a static variable is screwing you  
up.  You need for every sibling node to have the same parent, by  
definition, and for this to be accurate recorded in your database.

But only the first sibling node at any level of the tree will get the  
correct parent Guid because when you process the children for that first  
sibling, the algorithm overwrites the parent Guid value you're using.  The  
next sibling will get the Guid for the _last_ node you wrote to the  
database, not the actual parent of that node.

You should remove the code that assigns anything to tmpvar.NodeParent (and  
the variable itself, along with all of the other static variables, for  
that matter), and remove the conditional around the initialization of the  
Guids for non-root nodes.  As for the big problem of using  
"tmpvar.ParentGUID" (that is, the error that's really causing your  
problems), see my comments later in this post.

>             drNewRow["NodeGUID"] = tmpvar.NodeGUID;
>             drNewRow["ParentGUID"] = tmpvar.ParentGUID;
>             drNewRow["NodeText"] = node.Text;
>             drNewRow["NodeLevel"] = node.Level;

This part looks well enough to me.  Though, as I pointed out before: you  
need to store either the depth of the node ("node.Level") _or_ the parent  
of the node ("NodeGUID"), but there's no need to store both.  Not only is  
it wasteful, it just creates new opportunities for bugs.

>      // apply my db changes here
>
[quoted text clipped - 3 lines]
>                 SaveDBRecords(childNode);
>             }

See here?  As I explained before, you need to pass the parent Guid so that  
it can be used when writing the child nodes.  Doing it that way will  
provide for a local variable (the parameter itself) that always has the  
correct, current Guid for the parent of the currently-processed nodes.

So, instead of keeping the parent Guid in a static variable, just pass it  
as a parameter.  Then each node will always have an accurate parent Guid  
when it's written to the database.

>         }
>     }
>
> [...]
> You see Pete thinks the recursion is killing me, but it's not.

Yes, it is.

> Its trying to get these unique id's into the db
> and being able to tell who belongs to who.

If you were doing the recursion correctly, this would happen naturally.  
Because you aren't doing the recursion correctly, you aren't able to put  
the correct data into your database.

> I threw a wrench into the code when I added in two nodes
> with the same parent, that just ruined it for me.

Yup, it would.  Because the recursion isn't correct.

> To be honest with you, I really don't care how the tree layout is
> stored to the database.  But in order to
> store the layout into a db, you need the values of every single node
> that you are going to insert into the db.

No, you don't.  You only need the pertinent information for each group of  
siblings, and this is easy to provide with a very simple recursive  
algorithm.  When done correctly.

When it's not done correctly, yes...it can be quite a lot of trouble to  
get it right.

> And when I get ready to restore it from the db back to the tree, my
> next problem will be telling the node
> where to add the child.

Again, if the recursion is done correctly, this is very easy.  When you  
read a new node from the database, you add it to a dictionary indexed by  
the node's Guid.  Then to find the node's parent, you retrieve the parent  
node from that same dictionary, using the _parent_ Guid that was stored  
with the child.

(You could actually take advantage of knowing the order in which the nodes  
are written to the database in order to find the parent without the  
dictionary, but that would complicate your code and completely miss the  
point of using a Guid or other unique ID for each node in the database in  
the first place.  If you're going to rely on the order the nodes are put  
into the database, you might as well just store the depth for each node,  
rather than having an explicit reference to the parent).

> Adding the roots are easy, but this syntax is
> throwing me a curve.  If I knew
> delphi it would be so much easier to get used to this syntax.

It's not the syntax.  It's the basic structure of the algorithm.  The  
errors you've made would be the same in any similar language (i.e. one  
that requires that you actually understand the recursive nature of your  
data structure, unlike the apparent behavior of the C++Builder you're used  
to).

As long as you keep thinking this is a simple syntax problem, you will  
continue to have problems.

Pete
christery@gmail.com - 07 Jan 2008 15:43 GMT
just to get in the mood for recursion,
try to realize your problem/function in haskel first,
http://en.wikipedia.org/wiki/Haskell_(programming_language)
then look for syntax problems... with is the compilers job ;)
//CY
bob - 08 Jan 2008 10:33 GMT
>> Hi John,
>> How about deriving a class from treenode that has a 'NodeId' and a
[quoted text clipped - 6 lines]
>changed the structure of the tree, I realized that I am still lost.
>This is my code so far.

<Stuff Deleted>

Hi John,
Had a bit of  play with your code.
I think the problem is more a conceptual one rather than a coding one.
You are creating and placing the nodes on the tree then trying to  set
the node ids etc as you are writing to the database.

The following snippet shows what I was getting at;
1) Create nodes and add them to a collection (Don't try and place
them)
2) Use business logic to derive the ParentID for each node in the
collection (Ok I have simulated the business logic in the node
creation but you could iterate through the collection doing the same
thing)

3) Use a recursive routine to place the nodes on the tree.

This also simplifies the  database side.
You write the collection to the database.
You retrieve the collection from the database and feed it to the
Placenodes routine.

There are probably more elegant ways of doing this but
it works...
Code follow for;
Form
clsMyTreeNode

hth

Bob
P.S. Don't forget you can hang objects on the tag property of the
treenodes (allowing full separation of the data from the tree
structure) i.e. Database tree table and linked DataTables that provide
the Data.

****Form Code***
public partial class Form1 : Form
   {
       public Form1()
       {
           InitializeComponent();
           myCol = new List<clsMyTreeNode>();
       }
       private IEnumerable<clsMyTreeNode> myCol;
       private void button1_Click(object sender, EventArgs e)
       {
           //treeView1.BeginUpdate();
           treeView1.Nodes.Clear();
           for (int x = 1; x < 7; ++x)
           {
               clsMyTreeNode node = MakeNode(x);
               ((List<clsMyTreeNode>)myCol).Add(node);
           }
           //treeView1.ExpandAll();
           //treeView1.EndUpdate();
           MessageBox.Show("Nodes Made");

       }
       clsMyTreeNode MakeNode(int x)
       {
           clsMyTreeNode node = new clsMyTreeNode(x);
           switch(x)//'Business Logic' Simulation
           {
               case 1://root node
                   break;
               case 2:
                   node.ParentNodeId = 1;
                   break;
               case 3:
                   node.ParentNodeId = 2;
                   break;
               case 4://root node
                   break;
               case 5:
                   node.ParentNodeId = 4;
                   break;
               case 6:
                   node.ParentNodeId = 5;
                   break;
           }
           return node;
       }

       private void button2_Click(object sender, EventArgs e)
       {
           clsMyTreeNode root = new clsMyTreeNode(0);
           this.treeView1.Nodes.Add((TreeNode)root);
           PlaceNodes(ref root,(List<clsMyTreeNode>)myCol);
           this.treeView1.Refresh();
       }
       void PlaceNodes(ref clsMyTreeNode root,List<clsMyTreeNode>
nodes)
       {
           try
           {
               bool lglPlaced = false;
               for (; !lglPlaced; )
               {
                   lglPlaced = true;
                   foreach (clsMyTreeNode c in nodes)
                   {
                       if (!c.Placed)
                       {

                           // Place node and set status

                           CallRecursive(((clsMyTreeNode)(root)), c);

                           // debug.Writeline("CallRecursive " &
c.Text & " " & "Status " & c.Status.ToString)
                       }
                   }
                   foreach (clsMyTreeNode c in nodes)//if nodes not
placed around we go again
                   {
                       if (!c.Placed)
                       {
                           lglPlaced = false;
                       }
                   }
               }
           }
           catch (Exception ex)
           {
               
               MessageBox.Show("PlaceNodes " + ex.Message);
           }
       }
       private void CallRecursive(clsMyTreeNode Location,
clsMyTreeNode placement) {
       // Called by LoadTree
       //  Depth First traversal of tree, placing nodes and setting
statuses
       //clsMyTreeNode n;
       try {
           if (placement.Placed) {
               return;
           }
           foreach (clsMyTreeNode n in Location.Nodes) {
               // PlaceIt(n, placement)
               if (placement.Placed) {return;}
               CallRecursive(n, placement);
                   //return;
             
           }
           //Debug.Assert((placement.NodeId > 0));
           if (((Location.NodeId == placement.ParentNodeId)
                       && !placement.Placed)) {
               // Ready to place
               //placement.Text = placement.Name;
               Location.Nodes.Add(placement);
               placement.Placed = true;
               
           }
       }
       catch (ApplicationException ex) {
           MessageBox.Show("CallRecursive " + ex.Message);
       }
   }

   }
}
*****Derived Treenode Class *******
class clsMyTreeNode:TreeNode
   {
       
       public clsMyTreeNode()
       {

       }
       public clsMyTreeNode(int iNodeId)
       {
           
           myNodeId = iNodeId;
           myParentNodeId = 0;
           
       }
       private int myParentNodeId;
       public int ParentNodeId
       {
           get { return myParentNodeId; }
           set { myParentNodeId = value; }
       }
       private int myNodeId;

       public int NodeId
       {
           get { return myNodeId; }
           set { myNodeId = value; }
       }
       public override string ToString()
       {
           return "MyId is " + myNodeId + "Parent is " +
myParentNodeId;
       }
       private bool mlPlaced;

       public bool Placed
       {
           get { return mlPlaced; }
           set { mlPlaced = value;
           this.Text = "MyId is " + myNodeId + "Parent is " +
myParentNodeId;
       }
       public override object Clone()
       {
           clsMyTreeNode clone = (clsMyTreeNode)base.Clone();
           clone.NodeId = this.NodeId;
           clone.ParentNodeId = this.ParentNodeId;
           return clone;
       }
   
   }
John Rogers - 09 Jan 2008 00:07 GMT
> Hi John,
> Had a bit of  play with your code.
[quoted text clipped - 5 lines]
>
> The following snippet shows what I was getting at;

Thanks for the help Bob, I really do appreciate you helping me like
this.
Not being a professional programmer or writing code for a living makes
it difficult at times when you want to write something.  But thank God
for
the internet and for helpful people who help each other.

Thanks again.

John
bob - 10 Jan 2008 04:16 GMT
>> Hi John,
>> Had a bit of  play with your code.
[quoted text clipped - 16 lines]
>
>John

Your Welcome
regards
bob

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.