|
Hello,
I have a self joined table named:
Category. It contains 3 columns only: CAT_ID, PARENT_CAT_ID and Title.
I'm using a data reader to get the categories from the database as
follow:
while (dbReader.Read()) { Title = (string)dbReader["Title"]; CAT_ID = (int)dbReader["CAT_ID"]; PARENT_CAT_ID = (int)dbReader["PARENT_CAT_ID"];
// MessageBox.Show(Title); <- Working!!! }
Can anyone please tell me how can I populate a treeview recursivley with the data I pulled from the database?
Please note: 1. PARENT_CAT_ID = 0 for the root records. 2. Child records doesn't always follow parent record in the database query result, they can be mixed.
I would appreciate any help. |
| Angry Coder Sunday, July 09, 2006 5:54 PM |
Note that using recursion bombed with more than 4000 rows due to a stack overflow. better to do non recursive:
| | using (DbDataReader rdr = cmd.ExecuteReader()) { if (!rdr.HasRows) return; treeView1.BeginUpdate(); while (rdr.Read()) { TreeNode parent = GetNode(rdr["parent_cat_id"]); TreeNode current = GetNode(rdr["cat_id"]); current.Text = rdr["cat_title"].ToString(); if (parent != null) { treeView1.Nodes.Remove(current); parent.Nodes.Add(current); } } treeView1.EndUpdate(); }
|
but recursive: | | using (DbDataReader rdr = cmd.ExecuteReader()) { if (!rdr.HasRows) return; treeView1.BeginUpdate(); LoadNodes(rdr); treeView1.EndUpdate(); }
/* Here's the load routine */ private void LoadNodes(DbDataReader rdr) { if (!rdr.Read()) return; TreeNode parent = GetNode(rdr["parent_cat_id"]); TreeNode current = GetNode(rdr["cat_id"]); current.Text = rdr["cat_title"].ToString(); if (parent != null) { treeView1.Nodes.Remove(current); parent.Nodes.Add(current); } LoadNodes(rdr); }
|
Both ways requiretheGetNode method:
| | private TreeNode GetNode(object key) { if (DBNull.Value.Equals(key)) return null; TreeNode[] nodes = treeView1.Nodes.Find(key.ToString(), true); TreeNode node; if (nodes.Length == 0) node = treeView1.Nodes.Add(key.ToString(), string.Empty); else node = nodes[0]; return node; }
|
|
| Blair Allen Stark Monday, July 10, 2006 12:10 AM |
Hey angry. . .
One thing to consider, do you want to load the entire table at one time???
My suggestion would be to populate judiciously using a 'Lazy Load' pattern |
| Blair Allen Stark Sunday, July 09, 2006 8:21 PM |
Thanks Blair but lazy loading leads to additional database hits... and I like everything to be loaded at startup. 
|
| Angry Coder Sunday, July 09, 2006 8:29 PM |
well. . . give me a few moments and I will show one way, but still, if you dont hit the db every time a node is selected, how do you find changes?
unless this is not a concern, which is possible.
At any rate, 'Lazy Load' does not require a hit against the database everytime. Lazy Load against the in memoryDataTable.
and still it only depends on the size of the category table. . . If it is small, then a recursive load isn't that bad. . . though if it is a DataTable in memory, firstusing a Sort on cat_id, parent_id and a non recursive approach would be optimal.
Give me a moment and I will show a recursive approach. . .
Is this an coursework assignment???
Be honest. . . I will help you if it is!
But if it is, it doesn't do you any justice just giving you the answer as opposed to showing you how to approach any solution that requires recursion.
Make sense???
|
| Blair Allen Stark Sunday, July 09, 2006 8:45 PM |
Thanks for your follow-up. I need it for a program I'm writing for a client.
I'm already using a DataTable but I don't want that. I want to use a DataReader because it's faster.
Please note.. - I don't want to find changes. - Category tables contains around 500 rows.
|
| Angry Coder Sunday, July 09, 2006 9:06 PM |
Note that using recursion bombed with more than 4000 rows due to a stack overflow. better to do non recursive:
| | using (DbDataReader rdr = cmd.ExecuteReader()) { if (!rdr.HasRows) return; treeView1.BeginUpdate(); while (rdr.Read()) { TreeNode parent = GetNode(rdr["parent_cat_id"]); TreeNode current = GetNode(rdr["cat_id"]); current.Text = rdr["cat_title"].ToString(); if (parent != null) { treeView1.Nodes.Remove(current); parent.Nodes.Add(current); } } treeView1.EndUpdate(); }
|
but recursive: | | using (DbDataReader rdr = cmd.ExecuteReader()) { if (!rdr.HasRows) return; treeView1.BeginUpdate(); LoadNodes(rdr); treeView1.EndUpdate(); }
/* Here's the load routine */ private void LoadNodes(DbDataReader rdr) { if (!rdr.Read()) return; TreeNode parent = GetNode(rdr["parent_cat_id"]); TreeNode current = GetNode(rdr["cat_id"]); current.Text = rdr["cat_title"].ToString(); if (parent != null) { treeView1.Nodes.Remove(current); parent.Nodes.Add(current); } LoadNodes(rdr); }
|
Both ways requiretheGetNode method:
| | private TreeNode GetNode(object key) { if (DBNull.Value.Equals(key)) return null; TreeNode[] nodes = treeView1.Nodes.Find(key.ToString(), true); TreeNode node; if (nodes.Length == 0) node = treeView1.Nodes.Add(key.ToString(), string.Empty); else node = nodes[0]; return node; }
|
|
| Blair Allen Stark Monday, July 10, 2006 12:10 AM |
Worked. Many thanks. But I'm having an empty node as the root node of the treeview!  I want my main categories (the ones that has PARENT_ID = 0) to appear without a parent nodes. I really appreciate your help. |
| Angry Coder Monday, July 10, 2006 2:01 AM |
ahh. . I think it is because I used 'null' for top level, not '0'. . .
which is probably more proper. . . If it has no parent, parent is null.
A value of zero would mean parent iszero
But if you want zero to be top- change this line:
| | if (DBNull.Value.Equals(key)) return null;
|
to
| | if (DBNull.Value.Equals(key) || key.Equals(0)) return null;
|
|
| Blair Allen Stark Monday, July 10, 2006 4:40 AM |
Worked. Thank you so much.
|
| Angry Coder Monday, July 10, 2006 5:06 AM |
Hi, can you show what the SQL was for that Self-Join table? I've have a similar problem to solve.
|
| mdunn6 Tuesday, August 22, 2006 9:44 PM |
Hey Blair. I was reading this post and trying to figure out poplulating a winform Tree control with a self join table. I'm not getting anywhere. What does the SQL look like? I know it's an Inner Join with parent_id as foreign key. But when I tried the code, I didn't get the expected result. I just got 1 level of the Tree with no children levels.
Thanks in advance for any help. Best, Matt
|
| mdunn6 Tuesday, August 22, 2006 9:47 PM |
Thank you for this example. I'm close but not quite there. I have a single table where rows are arranged hierarchically via their PK / FK relationships.
The table is named NODE_DESCR: Row #1 has ID= 0, node_descrID=0 and has name="Scenario" Row #2 has ID= 1, node_descrID=0 and has name="Aircraft" Row #3 has ID= 1, node_descrID=0 and has name="Facility", etc.
I thought I'd be able to simply use the pattern above but refer to TreeNode parent = GetNode(rdr["ID"]); TreeNode current = GetNode(rdr["node_descrID"]); // or rdr["ID"] ??? current.Text = rdr["name"].ToString();
but when I exercise the following piece the appication seems to spin and I have to kill it. if (parent != null) { treeView1.Nodes.Remove(current); parent.Nodes.Add(current); }
Any chence you could help me get last piece? I tried both the recursive and non.
Thanks much, eric
|
| elmohler Tuesday, June 17, 2008 10:30 PM |
I answered my own question. When refering each row of the same table (NODE_DESCR) to other rows in a parent/child relationship *recursively* I had to assign the FK of the root node (Scenario) to 0 but assign the PK to 1. Aircraft and Facility are children of Scenario.
INSERT "NODE_DESCR"("ID","name","node_descrID") VALUES(1,'Scenario',1,0,0) go INSERT "NODE_DESCR"("ID","name","node_descrID") VALUES(2,'Scenario->Aircraft',1,1,1) go INSERT "NODE_DESCR"("ID","name","node_descrID") VALUES(3,'Scenario->Facility',1,2,1) go ........
This approach necessitates the use of the previously suggested approach of:
if (DBNull.Value.Equals(key) || key.Equals(0)) return null;
The only other issue for me was figuring out what arguments to use for GetNode(). Here's the approach that worked:
TreeNode parent = GetNode(myDataReader["node_descrID"]); TreeNode current = GetNode(myDataReader["ID"]); current.Text = myDataReader["name"].ToString();
Hopefully this helps newbies (or mortals) like me who have to work at it a bit before seeing the big picture. Thank you Blair for your example!!
Eric
|
| elmohler Wednesday, June 18, 2008 2:33 PM |
Here's the sample code Dim oDataSet As New DataSet Public Sub PopulateTree(ByVal ParentId As String, ByVal TVNode As TreeNode) Dim oDataView As New DataView(oDataSet.Tables(0), "ParentID='" & ParentId & "'", "DATA", DataViewRowState.OriginalRows) Dim oDataRow As DataRowView For Each oDataRow In oDataView Dim oTreeNode As New TreeNode(oDataRow("DATA")) Dim oComboBox As New ComboBox If TVNode Is Nothing Then Else TVNode.Nodes.Add(oTreeNode) PopulateTree(oDataRow("ID"), oTreeNode) End If Next End Sub Read In Details http://muruganad.com/ASP.NET/ASP-.NET-How-to-Populate-a-TreeView-Control-With-TreeNode-s-Using-recursive-algorithm-or-recursion-.html Thanks Murugan Andezuthu Dharmaratnam |
| Murugan Andezuthu Dharmaratnam Sunday, August 30, 2009 1:16 PM |