Windows Develop Bookmark and Share   
 index > Windows Forms General > Populating a treeview recursively
 

Populating a treeview recursively

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

You can use google to search for other answers

Custom Search

More Threads

• Display Text in TextBox at Cursor Position
• Subsetting a List<>
• Combobox Add Items
• tab and IsInputKey
• diabling CTRL + Tab in tab control
• Embedding file objects to word forms
• Passing events on to covered controls
• pressing arrow keys as hot-keys changes control focus
• What is a Dat file? In the binary writer context
• .Net App Updater and Version numbers