Sunday, September 9, 2007

Let's Build Tree

Tree is one data structure that fascinates me like many other software engineers. So let me try and explore how to populate trees.

here i am trying to load a treeview (system.windows.forms.treeview) control provided by Microsoft .net 2003.

What kind of Database table can be bound to trees?
the above question would be a good start. Basically in most of the cases we will be loading a tree with data from a table which has self referencing data. It means rows which refer other rows as parent records. Along with other attribute columns the below columns is a must in a self referencing table.
ID - primary key of the table
Name - a basic human readable attribute of the entity/row
Parent_ID - column to hold the parents ID to which this row is related.
Parent_Name - (optional) name of the parent to which the child row is related.
In addition it may contain many other columns which describe the entity.

So now that we have defined a suitable candidate to be loaded into the treeview how do we load it. There are different methods to load the control with data. Some people write a recursive function to construct the tree control. This approach is good if there are not many childnodes associated with a node and not much of data to be loaded at the first place. But since in most of the cases it is not the case i prefer to load the control with data on demand. So let me try and explain how to do this.

I will first define a Datatable object and load it with all the data that is to be loaded into the treeview control. let me name it as "treeData".
when the treeview is first shown to the user it must have atleast one node with an expand button (usually + sign) depending on whether the node contains child nodes or not. So let us create two functions. one to add a root node AddRoot and the other to check whether a given node has child nodes or not.

Private Sub AddRoot()
Dim row As DataRow
Dim filter As String = "parent_name = 'ROOT'"
Try
If treeData.Rows.Count > 0 Then
If Not treeData.Select(filter).Length = 1 Then
MessageBox.Show("More than one root or no root is present")
Else
row = treeData.Select(filter).GetValue(0)
trvwLoc.Nodes.Add(row("name"))
If ChildExists(row("name")) Then
trvwLoc.Nodes(0).Nodes.Add("dummy")
End If
End If
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally

End Try
End Sub


what i am trying to do in the above lines of code is to identify the root record. this can be done by updating the parent_name column of the table with a value say 'ROOT'. this will be our first root element. i ofcourse have made a check to make sure that the table must contain only one root element. in cases where more than one root element can be present than you can change the code accordingly to construct multiple root nodes.
ChildExists is a user defined function which will accept a string value and return a boolean result. the purpose of this function is to check whether the node being added contains child nodes and if so add a 'dummy' node to it. this 'dummy' node is needed so that the node being added gets a plus symbol to it informing the user that the node contains child elements. when the user expands this node we will add its relevant child nodes. Below is the definition of the ChildExists function

Private Function ChildExists(ByVal parent As String) As Boolean
Dim filter As String = "parent_mnem = '" & parent & "'"
If treeData.Select(filter).Length > 0 Then
Return True
Else
Return False
End If
End Function


In both the above functions i am using Select method of a DataTable object to apply filters to see whether the node contains any record.

Now let us add code to add the child nodes for a given tree node. Below is the code to do it.

Private Sub AddChildNodes(ByVal parent As TreeNode)
Dim row As DataRow
If parent.Nodes(0).Text = "dummy" Then
parent.Nodes.RemoveAt(0)
Dim filter As String = "parent_name = '" & parent.Text & "'"
Dim sort As String = "name asc"
For Each row In IspLoc1.SM_ISP_LOCATION.Select(filter, sort)
Dim childnode As New TreeNode
childnode.Text = CType(row("name"), String)
If ChildExists(childnode.Text) Then
childnode.Nodes.Add("dummy")
End If
parent.Nodes.Add(childnode)
Next
End If
End Sub

All i am doing in the above code is to check whether the node has child nodes (dummy node). if it does get the children of the node in a sorted manner ( again i am using one of the overridden flavours of Select method ). Make sure to remove the dummy node as the users dont want to see it :) and when adding each child node check whether that node has any more children.

We are just two lines away from completing the implementation and that is to add event handler to call the AddChildNodes method. we will call this on the BeforeExpand event handler of the tree view control. Below is the code for the same.

Private Sub trvwLoc_BeforeExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles trvwLoc.BeforeExpand
trvwLoc.SelectedNode = e.Node
AddChildNodes(trvwLoc.SelectedNode)
End Sub

in my code trvwLoc is the name of the tree view control.

So thats the end of it. Let me know if you face any issues. Please be ware that there is one disadvantage in this approach and that is the memory overhead. But i think in the current days performance is of more importance as we have too much of cheap memory :).

happy Coding.. Go and build your tree..

No comments: