> [...]
> // 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
>> 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