Click here to Skip to main content
Click here to Skip to main content

Auto Binding a TreeView control from a self referencing table

By , 16 Apr 2007
 
Screenshot - AutoBindingTreeView.gif

Introduction

This article will show how to implement a custom ASP.NET TreeView control that supports auto binding from a self referencing table. The control supports binding from a Data Source control or programmatically using the Data Source property.

Background

This article is a continuation to my previous article showing useful functionalities that the TreeView control lacks. The previous article demonstrated applying CSS and adding custom attributes to tree nodes. In this article, Auto Binding a TreeView control from a Self Referencing Table is discussed.

What is a Self Referencing Table?

A self referencing table contains a relation where one of its columns references another column in the same table. For example:

Self Referencing table

In the figure above, Parent ID column references the ID column meaning that the Parent ID is an ID forming hierarchical data.

Auto Binding

ASP.NET TreeView only supports hierarchical datasource controls. To work around that, we create a data-bound control class AutoBindingTree that extends the base class DataBoundControl so that we can override PerformSelect and PerformDataBinding events. Click here for more information on creating custom data-bound controls.

The PerformDataBinding takes an IEnumerable as a parameter representing the Data retrieved from the Data Source. This parameter is used to create a DataView since it implements IEnumerable.The DataView is then use to create a DataSet that maintains the Self Reference Relationship and is then used to populate the TreeView. The DataSet uses three mandatory properties:

  1. DataTextField: The name of the column holding the Node Text Value
  2. DataFieldID: The name of the Column holding the ID value
  3. DataFieldParentID: The name of the Column holding the ParentID value
Protected Overrides Sub PerformDataBinding(ByVal oSourceData As IEnumerable)
    MyBase.PerformDataBinding(oSourceData)

    ' Verify data exists.
    If Not (oSourceData Is Nothing) Then
    Dim oView As DataView = oSourceData
    Dim oTable As DataTable = oView.Table
    Dim oDS As DataSet = New DataSet()
    oDS.Tables.Add(oTable.Copy())

        'Create a Relation Between the ID Column and Parent Column
        If oDS.Relations.Contains("SelfRefenceRelation") = False Then
        oDS.Relations.Add("SelfRefenceRelation", _
            oDS.Tables(0).Columns(DataFieldID), _
            oDS.Tables(0).Columns(DataFieldParentID))
        End If

        oTable.Dispose()
        oTable = Nothing

        ViewState("TreeData") = oDS
        LoadTreeView(oDS)

        oDS.Dispose()
        oDS = Nothing
    End If
        End Sub

Once the DataSet is created, LoadTreeView is called to populate the Treeview:

        
Private Sub LoadTreeView(ByVal oDS As DataSet)
    Dim oTreeView As TreeView = New TreeView()
    Dim oDataRow As DataRow
    For Each oDataRow In oDS.Tables(0).Rows
        'Find Root Node,A root node has ParentID NULL
        If oDataRow.IsNull(DataFieldParentID) Then
        'Create Parent Node and add to tree
        Dim oNode As New TreeNode()
        oNode.Text = oDataRow(DataTextField).ToString()
        oNode.Value = oDataRow(DataFieldID).ToString()
        oNode.NavigateUrl = oDataRow("NavigateURL").ToString()
        oTreeView.Nodes.Add(oNode)

        'Recursively Populate From root
        RecursivelyLoadTree(oDataRow, oNode)
        End If
    Next oDataRow
    Controls.Add(oTreeView)
    oDS.Dispose()
    oDS = Nothing
End Sub 'LoadTreeView

First of all Root elements (ParentID = NULL) are identified, added to the tree and then the function RecursivelyLoadTree is called to recursively create child elements:

Private Sub RecursivelyLoadTree(ByVal oDataRow As DataRow, _
    ByRef oNode As TreeNode)
    Dim oChildRow As DataRow
    'returns an array of DataRow objects representing the child view 
    For Each oChildRow In oDataRow.GetChildRows("SelfRefenceRelation")
        'Create child node and add to Parent
        Dim oChildNode As New TreeNode()
        oChildNode.Text = oChildRow(DataTextField).ToString()
        oChildNode.Value = oChildRow(DataFieldID).ToString()
        oChildNode.NavigateUrl = _
            oChildRow("NavigateURL").ToString()
        oNode.ChildNodes.Add(oChildNode)
        'Repeat for each child
        RecursivelyLoadTree(oChildRow, oChildNode)
    Next oChildRow
End Sub 'RecursivelyLoadTree

Using the code

The easiest way to use the control is to either bind it to a Data Source control (DataSourceControl.aspx):

<%@ Register TagPrefix="CPArticles" Namespace="CPArticles"%>

<CPArticles:AutoBindingTree runat="server" ID="TestCtrl" 
    DataTextField="Text" 
    DataFieldID="ID" 
    DataFieldParentID="ParentID" 
    DataSourceID="SqlDataSource1" 
 />

  <asp:SqlDataSource id="SqlDataSource1" runat="server" 
    ConnectionString="<%$ ConnectionStrings:ConnectionString %>" 
        SelectCommand="SELECT * FROM SelfReferenceTable" 
        SelectCommandType="Text" 
 />

or by programmatically setting the DataSource property(DataBind.aspx):

     
TestCtrl.DataFieldID = "ID"
TestCtrl.DataFieldParentID = "ParentID"
TestCtrl.DataTextField = "Text"
TestCtrl.DataSource = oDs
TestCtrl.DataBind()

Both samples and the SQL Script are in the demo application.

History

  • April 15, 2007: Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Bassam Saoud
Software Developer (Senior) Delcan Corporation
United States United States
Bassam Saoud is a software developer with more then 6+ years of experience. He has masters in Computer Science from American University of Technology and is currently doing Masters in Business Administration at Colorado Heights University. He started Programming with C++ and VB and now working with ASP.NET VB.NET/C#, SQL Server 2000/2005.
 
His interests are mainly politics, Basketball and Traveling.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionOnSelectedNodeChangedmemberkerm795-Apr-11 0:16 
Hi
first off nice article
 
How do i implement the OnSelectedNodeChanged i do not want to use the NavigateURL   Smile | :)
 
i have been looking at adding a Custom Event but havent found out how to
AnswerRe: OnSelectedNodeChangedmemberahussiny24-Mar-12 3:05 
did you know How do i implement the OnSelectedNodeChanged i do not want to use the NavigateURL ?????????????????????????????????????????
GeneralIs there any way to save this to xml? [modified]memberKaeghl29-Jan-09 7:25 
I have tried:
 
private void button1_Click(object sender, EventArgs e)
{
 XmlDocument d = new XmlDocument();
 XmlNode n = d.CreateNode(XmlNodeType.Element,"root","");
 foreach (TreeNode t in TestCtrl.Nodes)
 {
  n.AppendChild(getXmlNode(t, d));
 }
 d.AppendChild(n);
 d.Save(AppDomain.CurrentDomain.BaseDirectory+"out.xml");
}
private XmlNode getXmlNode(TreeNode tnode,XmlDocument d)
{
 XmlNode n = d.CreateNode(XmlNodeType.Element, tnode.Text, "");
 
 foreach (TreeNode t in tnode.Nodes)
 {
  n.AppendChild(getXmlNode(t,d));
 }
 return n;}
 
But since this is inherited from a databind I have no way to check the nodes.
Any help?
 
modified on Thursday, January 29, 2009 1:35 PM

GeneralGenerating Treeview from datatablememberamitabha200712-Jan-09 22:31 
I am having following datatable
Audit Zone_Name District_Name Town_Name
East Kolkata barasat
East Kolkata dumdum
West Mumbai Kalyan
West Mumbai Thane
West Pune Pune1
West Pune Pune2
West Mumbai Dombivili
 
now i want to pass this data table in a procedure which will make a Tree which will contain
Zone_Name's as the roots, District_Names as Child, Town_Name as Grand Child.
 
private void insertnodes(DataTable dt_Table)
 
{
TreeNode t, tn = null;
ArrayList arr_List = new ArrayList();
for (int i = 0; i <= dt_Table.Rows.Count - 1; i++)
{
for (int j = 0; j <= dt_Table.Columns.Count - 1; j++)
{
if (arr_List.IndexOf(dt_Table.Rows[i][j]) == -1)
{
arr_List.Add(dt_Table.Rows[i][j]);
if (j == 0)
{
t = new TreeNode(dt_Table.Rows[i][j].ToString());
treeView1.Nodes.Add(t);
}
else
{
t = new TreeNode(dt_Table.Rows[i][j].ToString());

}
 
}
 
else
{
continue;
}
 
}
 
}
 
The main thing is now i want to know what to write in the else part ie
else
{
t = new TreeNode(dt_Table.Rows[i][j].ToString());
}
so that we get the desired output. Please try solving the problem by using datatable only since, i want to make a generalised code which will make the desired treeview once i pass the datatable in it..
 
Waiting for answer...
QuestionTreeView From Datatablememberamitabha200712-Jan-09 22:29 
I am having following datatable
Audit Zone_Name District_Name Town_Name
East Kolkata barasat
East Kolkata dumdum
West Mumbai Kalyan
West Mumbai Thane
West Pune Pune1
West Pune Pune2
West Mumbai Dombivili
 
now i want to pass this data table in a procedure which will make a Tree which will contain
Zone_Name's as the roots, District_Names as Child, Town_Name as Grand Child.
 
private void insertnodes(DataTable dt_Table)
 
{
TreeNode t, tn = null;
ArrayList arr_List = new ArrayList();
for (int i = 0; i <= dt_Table.Rows.Count - 1; i++)
{
for (int j = 0; j <= dt_Table.Columns.Count - 1; j++)
{
if (arr_List.IndexOf(dt_Table.Rows[i][j]) == -1)
{
arr_List.Add(dt_Table.Rows[i][j]);
if (j == 0)
{
t = new TreeNode(dt_Table.Rows[i][j].ToString());
treeView1.Nodes.Add(t);
}
else
{
t = new TreeNode(dt_Table.Rows[i][j].ToString());

}
 
}
 
else
{
continue;
}
 
}
 
}
 
The main thing is now i want to know what to write in the else part ie
else
{
t = new TreeNode(dt_Table.Rows[i][j].ToString());
}
so that we get the desired output. Please try solving the problem by using datatable only since, i want to make a generalised code which will make the desired treeview once i pass the datatable in it..
 
Waiting for answer...
GeneralYou can achieve this with one line of codememberRalph Varjabedian23-Apr-08 1:15 
http://www.varjabedian.net/archive/2008/04/22/binding-asp.net-treeview-to-a-dataset-or-an-objectdatasource.aspx
 
Ralph Varjabedian
Chief Software specialist

GeneralRe: You can achieve this with one line of codememberBassam Saoud28-Jul-08 4:44 
I am looking at the source code of HierarchicalDataSet class. Thats not exactly one line of code now is it Smile | :)
 
But the solution implemented is cool. Thanks for sharing your code with us.
GeneralRe: You can achieve this with one line of codememberRalph Varjabedian28-Jul-08 5:14 
Well you are correct Smile | :) in all fairness, it is not just one line of code... However the developer will only need one line of code. Cheers.
 
Ralph Varjabedian
Chief Software specialist

My Blog
Bytesurge.com



GeneralRe: You can achieve this with one line of codememberBassam Saoud28-Jul-08 5:17 
was looking at your profile, you are from Lebanon? I hope you guys are doing fine with all the sh*t that is happening.
 
Be Safe !
GeneralRe: You can achieve this with one line of codememberRalph Varjabedian29-Jul-08 0:02 
Thanks, really. However since a while ago things are becoming much better and it looks optimistic.
Thanks again.
 
Ralph Varjabedian
Chief Software specialist

My Blog
Bytesurge.com



GeneralExcellent stuffmembergeckex3-Feb-08 13:12 
Hi - converted this into C# and it works a treat!
GeneralRe: Excellent stuffmemberBassam Saoud4-Feb-08 3:56 
Thanks
 
Bassam
QuestionHow to extend a TreeView Control with this binding and what you've done in your other article?memberAnksuNico20-Nov-07 8:24 
Hi,
 
All is in the title.
 
I want to bind a treeview with your method, Adding Custom Attributes and applying CSS to Nodes in a TreeView Control and get all possibilities of a normal TreeView.
 
How is it possible?
 
Thanks in advance.
AnswerRe: How to extend a TreeView Control with this binding and what you've done in your other article?memberBassam Saoud20-Nov-07 8:33 
Add both code to your project Smile | :) Try it and let me know if you have any problems.Dont forget to vote for the articles if you like them
 
Bassam
GeneralI want to set First of all Root elements (ParentID = 0),how edit the code [modified]membermaseccc9-Sep-07 23:24 
I convert to code from vb to C#
the source code is :
 
public class AutoBindingTree : DataBoundControl
{
 
// ''
// '' The name of the column holding the Text Value
// ''

// ''
// ''
// ''
public string DataTextField
{
get
{
object oText = ViewState["DataTextField"];
if ((oText == null))
{
return String.Empty;
}
else
{
return oText.ToString();
}
}
set
{
ViewState["DataTextField"] = value;
if (Initialized)
{
OnDataPropertyChanged();
}
}
}
 
public string DataFieldID
{
get
{
object oID = ViewState["DataFieldID"];
if ((oID == null))
{
return String.Empty;
}
else
{
return oID.ToString();
}
}
set
{
ViewState["DataFieldID"] = value;
}
}
 
public string DataFieldParentID
{
get
{
object oParentID = ViewState["DataFieldParentID"];
if ((oParentID == null))
{
return String.Empty;
}
else
{
return oParentID.ToString();
}
}
set
{
ViewState["DataFieldParentID"] = value;
}
}
 
protected override void PerformSelect()
{
// Call OnDataBinding here if bound to a data source using the
// DataSource property (instead of a DataSourceID), because the
// databinding statement is evaluated before the call to GetData.
if (!IsBoundUsingDataSourceID)
{
OnDataBinding(EventArgs.Empty);
}
// The GetData method retrieves the DataSourceView object from
// the IDataSource associated with the data-bound control.
GetData().Select(CreateDataSourceSelectArguments(), OnDataSourceViewSelectCallback);
// The PerformDataBinding method has completed.
RequiresDataBinding = false;
MarkAsDataBound();
// Raise the DataBound event.
OnDataBound(EventArgs.Empty);
}
 
private void OnDataSourceViewSelectCallback(IEnumerable retrievedData)
{
// Call OnDataBinding only if it has not already been
// called in the PerformSelect method.
if (IsBoundUsingDataSourceID)
{
OnDataBinding(EventArgs.Empty);
}
// The PerformDataBinding method binds the data in the
// retrievedData collection to elements of the data-bound control.
PerformDataBinding(retrievedData);
}
 
protected override void PerformDataBinding(IEnumerable oSourceData)
{
base.PerformDataBinding(oSourceData);
// Verify data exists.
if (!(oSourceData == null))
{
DataView oView = (DataView)oSourceData;
DataTable oTable = oView.Table;
DataSet oDS = new DataSet();
oDS.Tables.Add(oTable.Copy());
// Create a Relation Between the ID Column and Parent Column
if ((oDS.Relations.Contains("SelfRefenceRelation") == false))
{
oDS.Relations.Add("SelfRefenceRelation", oDS.Tables[0].Columns[DataFieldID], oDS.Tables[0].Columns[DataFieldParentID]);
}
oTable.Dispose();
oTable = null;
ViewState["TreeData"] = oDS;
LoadTreeView(oDS);
oDS.Dispose();
oDS = null;
}
}
 
// ''
// '' Populate Tree View
// ''

// '' Data Set
// ''
private void LoadTreeView(DataSet oDS)
{
TreeView oTreeView = new TreeView();
// DataRow oDataRow;
foreach (DataRow oDataRow in oDS.Tables[0].Rows)
{
// Find Root Node,A root node has ParentID NULL
// if (oDataRow.IsNull(DataFieldParentID))
if (oDataRow[DataFieldParentID].ToString() =="0")
{
// Create Parent Node and add to tree
TreeNode oNode = new TreeNode();
oNode.Text = oDataRow[DataTextField].ToString();
oNode.Value = oDataRow[DataFieldID].ToString();
oNode.NavigateUrl = oDataRow["NavigateURL"].ToString();
oTreeView.Nodes.Add(oNode);
// Recurively Populate From root
RecursivelyLoadTree(oDataRow, ref oNode);
}
}
Controls.Add(oTreeView);
oDS.Dispose();
oDS = null;
}
 
// LoadTreeView
// ''
// '' Recursively Load ChildNodes for each Node using the Dataset relation
// ''

// '' Data Row
// '' Parent Node
// ''
private void RecursivelyLoadTree(DataRow oDataRow, ref TreeNode oNode)
{
// DataRow oChildRow;
// returns an array of DataRow objects representing the child view
foreach (DataRow oChildRow in oDataRow.GetChildRows("SelfRefenceRelation"))
{
// Create child node and add to Parent
TreeNode oChildNode = new TreeNode();
oChildNode.Text = oChildRow[DataTextField].ToString();
oChildNode.Value = oChildRow[DataFieldID].ToString();
oChildNode.NavigateUrl = oChildRow["NavigateURL"].ToString();
oNode.ChildNodes.Add(oChildNode);
// Repeat for each child
RecursivelyLoadTree(oChildRow, ref oChildNode);
}
}
 
protected override object SaveViewState()
{
object baseState = base.SaveViewState();
object[] allStates = new object[2];
allStates[0] = baseState;
allStates[1] = ViewState["TreeData"];
return allStates;
}
 
protected override void LoadViewState(object savedState)
{
if (!(savedState == null))
{
object[] myState = ((object[])(savedState));
if (!(myState[0] == null))
{
base.LoadViewState(myState[0]);
}
if (!(myState[1] == null))
{
ViewState["TreeData"] = ((DataSet)(myState[1]));
}
if (!(ViewState["TreeData"] == null))
{
LoadTreeView(((DataSet)(ViewState["TreeData"])));
}
}
}
}
 

-- modified at 23:33 Monday 10th September, 2007
GeneralRe: I want to set First of all Root elements (ParentID = 0),who to edit the codememberBassam Saoud10-Sep-07 4:41 
I am not sure I understand your question, It is enough to set the ParentID to NULL to become a root element.You can do that from your database table, you don't need to change anything in code.
GeneralRe: I want to set First of all Root elements (ParentID = 0),who to edit the code [modified]membermaseccc10-Sep-07 16:21 
thanks for reply,
 
i want to set the root element's ParentID to 0 ,not set to null,how to modify the code
i modify the code
 
" if (oDataRow.IsNull(DataFieldParentID))"
to
"if (oDataRow[DataFieldParentID].ToString() =="0")"
but the error is :This constraint cannot be enabled as not all values have corresponding parent values.
 
thanks!
 

-- modified at 22:43 Monday 10th September, 2007
GeneralRe: I want to set First of all Root elements (ParentID = 0),who to edit the code [modified]memberAnksuNico20-Nov-07 6:33 
Just modify your SQL Request like that :
 
NULLIF (ParentID, 0)
Questionsource code and databasemembercedey5-Aug-07 19:14 
Where can I download the source code and database?
Cameron
AnswerRe: source code and databasememberBassam Saoud6-Aug-07 1:13 
cedey wrote:
Where can I download the source code and database?

There is a download link at the beginning of the article, incase you missed it: Source Code[^]. You must first login to CP before you download...
 
Enjoy!
GeneralCreate Tag "Target"memberGodinho Ferreira16-Apr-07 13:08 
Hello
 
Is possible to create a html tag target="...".
I need use iframe control.
 
Thanks
Carlos Ferreira
GeneralCreat TargetmemberGodinho Ferreira16-Apr-07 13:06 
Hello
 
how i can creat de tag "target", i want to use iframe.
 
Thanks
Carlos Ferreira
GeneralRe: Creat Targetmemberxanth19-Apr-07 7:56 
TreeNode n;
n.Target = "??????"
GeneralRe: Creat Targetmembertomot15-Oct-07 1:22 
For any additional data you can derive from TreeNode and add your own properties
 
Public Class MyTreeViewNode
Inherits TreeNode
 
Public Var1 As Integer
Public Var2 As String
 
End Class
 
then call your properties
 
Dim oChildNode As New MyTreeViewNode
oChildNode.Var1 = 10
oChildNode.Var2 = "whatever"

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130617.1 | Last Updated 16 Apr 2007
Article Copyright 2007 by Bassam Saoud
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid