Use ASP.NET 2.0's TreeView to Display Hierarchical Data

Real-world Web sites need professional navigation structures. In classic ASP, developers used either custom code or third-party solutions to provide navigational elements such as TreeViews and Menus. Fortunately, ASP.NET 2.0 comes with these controls built-in. They provide not only good-looking navigational structure but also save developers from needing to write lots of code and script.

This article examines the TreeView control, which you use to display hierarchical data. For example, suppose your Web site sells products that belong in various categories. You may want to arrange the product information on category and subcategory bases. The same categorization is applicable for the Web site pages. You may want to display them in certain categories such as Products, Services, and Support. TreeView comes handy in all such cases.

Some Basics

Before delving into the details of TreeView, get acquainted with the basics first.

The TreeView control is represented by the mark tag <asp:TreeView> and the corresponding class System.Web.UI.WebControls.TreeView. TreeView consists of one or more nodes represented by the TreeNode class. The tree nodes are represented in the markup by <asp:TreeNode> tag. The following table lists some of the important properties of TreeNode class:

Property Description
Text This property specifies the text that appears for that node.
Value This property specifies a value that can be accessed programmatically when the node is clicked or expanded.
NavigateUrl This property points to the URL of the page where the user is navigated after clicking on the node.
Target This property indicates the destination for the URL represented by the NavigateUrl property (new windows, current windows, and so forth).
ImageUrl This property points to the image file that is displayed for the node.
ToolTip This property indicates the text that is displayed when the user hovers the mouse over the TreeNode text.
ImageToolTip This property indicates the text that is displayed when the user hovers the mouse over the TreeNode image.

Adding Nodes Manually

Now that you know some basics of TreeView and TreeNode, you can follow the first example, which illustrates how to set up the TreeView manually.

Create a new Web site in VS.NET or Visual Web Developer (VWD). Add a Web form called Example1.aspx. Drag and drop a TreeView control and a Label control on it. From the smart tags of TreeView (see Figure 1), select "Edit Nodes".

Figure 1: Smart Tag of TreeView Control

When you select "Edit Nodes..." the IDE will open the TreeView Node Editor dialog (see Figure 2).

Figure 2: TreeView Node Editor Dialog

Using the toolbar of the dialog, add tree nodes as shown in Figure 2. For each node, set Text and NavigateURL properties to appropriate values. In this example, you will enter the NavigateUrl property to ~/example1.aspx?id=1 for the ASP.NET node, ~/example1.aspx?id=2 for the Web services node, and so on. Once you finish the visual design of the TreeView, the resultant markup generated should resemble the following:

<asp:TreeView ID="TreeView1" runat="server">
<Nodes>
   <asp:TreeNode Text="Book Categories" Value="Book Categories">
      <asp:TreeNode Text="Web Development" Value="Web Development">
      <asp:TreeNode Text="ASP.NET" Value="ASP.NET"
                    NavigateUrl="~/example1.aspx?id=1"></asp:TreeNode>
      <asp:TreeNode Text="Web Services" Value="Web Services"
                    NavigateUrl="~/example1.aspx?id=2"></asp:TreeNode>
      <asp:TreeNode Text="JSP" Value="JSP"
                    NavigateUrl="~/example1.aspx?id=3"></asp:TreeNode>
   </asp:TreeNode>
   <asp:TreeNode Text="Windows Development"
                 Value="Windows Development">
      <asp:TreeNode Text="Windows Forms" Value="Windows Forms"
                    NavigateUrl="~/example1.aspx?id=4"></asp:TreeNode>
      <asp:TreeNode Text="ActiveX Controls" Value="ActiveX Controls"
                    NavigateUrl="~/example1.aspx?id=5"></asp:TreeNode>
      <asp:TreeNode Text="Smart Client" Value="Smart Client"
                    NavigateUrl="~/example1.aspx?id=6"></asp:TreeNode>
      </asp:TreeNode>
   <asp:TreeNode Text="Component Development"
                 Value="Component Development">
      <asp:TreeNode Text="COM" Value="COM"
                    NavigateUrl="~/example1.aspx?id=7"></asp:TreeNode>
      <asp:TreeNode Text="DCOM" Value="DCOM"
                    NavigateUrl="~/example1.aspx?id=8"></asp:TreeNode>
      <asp:TreeNode Text="Remoting" Value="Remoting"
                    NavigateUrl="~/example1.aspx?id=9"></asp:TreeNode>
      </asp:TreeNode>
   </asp:TreeNode>
</Nodes>
</asp:TreeView>

As you can see, the Value property contains the same text as the Text property by default. You can change it if you want.

Now, write the following code in the SelectedNodeChanged event of the TreeView control:

protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
{
   Label1.Text = "You Selected " + TreeView1.SelectedNode.Text
                                 + " category";
}

The SelectedNodeChanged event is raised when you click on the TreeNode. In the code, you simply set the Text property of the label to some message. Notice how you retrieved the Text of the selected node. In a similar fashion, you can retrieve the Value property of the selected node.

If you run the sample at this stage, it will work as expected for nodes that do not have the NavigateUrl property set and fail for the other nodes. This happens because the SelectedNodeChanged event is raised only for the nodes that do not have NavigateUrl property set. When you set the NavigateUrl property, you are directly navigated to the destination page. To trap such a selection, write the following code in the Page_Load event of the Web form:

protected void Page_Load(object sender, EventArgs e)
{
   switch (Request.QueryString["id"])
   {
      case "1":
         Label1.Text="You selected ASP.NET category";
         break;
      case "2":
         Label1.Text="You selected Web Services category";
         break;
      case "3":
         Label1.Text="You selected JSP category";
         break;
      case "4":
         Label1.Text="You selected Windows Forms category";
         break;
      case "5":
         Label1.Text="You selected ActiveX category";
         break;
      case "6":
         Label1.Text="You selected Smart Client category";
         break;
      case "7":
         Label1.Text="You selected COM category";
         break;
      case "8":
         Label1.Text="You selected DCOM category";
         break;
      case "9":
         Label1.Text = "You selected Remoting category";
         break;
   }
}

Remember that you set the NavigateUrl property of various nodes to Example1.aspx, passing a query string parameter called "id". In the Page_Load event, you trap this parameter to decide which link was clicked. Depending on the value of this query string parameter, you display different messages. Figure 3 shows a sample run of your Web form.

Figure 3: Sample Run of Example1.aspx

Adding Nodes Programmatically

The previous example added nodes to TreeView at design time. However, in the real world, many times the data that you want to display in the TreeView resides in a database like SQL Server or Oracle. That's when you need to add tree nodes via code.

In the following example, you will build a TreeView that will display all the customers from the Customers table of the Northwind database. Selecting a customer node will add its order IDs as child nodes. This example demonstrates three things:

  1. Creating and adding tree nodes on the fly
  2. Improving performance with "on demand" population
  3. Filling the TreeView with data from the SQL Server database

Add a new Web form to the Web site you created previously. Drag and drop a TreeView control and add a root node to it with the Text property set to "Customers". Be sure to set its PopulateOnDemand property to true (see Figure 4). This property plays a significant role in dynamically generated TreeViews. Many times, it is not practical to populate all the nodes and child nodes of TreeView in advance. Doing so can drastically affect the performance of your Web form. The PopulateOnDemand property helps you handle such situations. When you set this property to true, clicking that node raises an event called TreeNodePopulate. Inside the TreeNodePopulate event, you write the logic to populate child nodes of the node. This way, various nodes are populated on demand.

Figure 4: Setting the PopulateOnDemand Property

Use ASP.NET 2.0's TreeView to Display Hierarchical Data

Next, write a method called FillCustomers in the code behind. This method will be called when the user expands the root node (Customers). The method looks like this:

private void FillCustomers(TreeNode parent)
{
   DataSet ds = GetDataSet("select customerid,companyname
                            from customers order by companyname");
   foreach(DataRow row in ds.Tables[0].Rows)
   {
      TreeNode node=new TreeNode();
      node.Text=row["companyname"].ToString();
      node.Value=row["customerid"].ToString();
      node.PopulateOnDemand = true;
      node.SelectAction = TreeNodeSelectAction.SelectExpand;
      parent.ChildNodes.Add(node);
   }
}

The FillCustomers method makes use of a helper function called GetDataSet that accepts the SQL query and returns a DataSet filled with the results of the supplied query. The following code shows the GetDataSet function:

private DataSet GetDataSet(string sql)
{
   string connstr    = @"data source=.\sqlexpress;
   initial catalog   = northwind;integrated security=true";
   SqlDataAdapter da = new SqlDataAdapter(sql, connstr);
   DataSet ds        = new DataSet();
   da.Fill(ds);
   return ds;
}

Here, you create an SqlDataAdapter and fill a DataSet with the results of the supplied query. You then return the DataSet back to the caller. By using this method, the FillCustomers() method retrieves a DataSet filled with all customers. You then iterate through various rows. For each row, you create an instance of the TreeNode class. Note how you set the Text, Value, and PopulateOnDemand properties of these nodes. This time, the Text and Value properties contain different data. The Text property shows the CompanyName, where as the Value property contains CustomerID. You set PopulateOnDemand to true because, when a user expands these nodes, you further want to populate OrderIDs as their child nodes. Also note that you have set the SelectAction property to "SelectExpand". The SelectAction property is an enumeration of type TreeNodeSelectAction. This property governs the events that are raised by the newly added node. The following table lists the possible values of this property and the associated events.

TreeNodeSelectAction Value Description
Expand Raises either TreeNodeExpanded or TreeNodeCollapsed event, depending on the current expand or collapse state of the node
Select Raises SelectedNodeChanged event when a node is selected
SelectExpand Raises SelectedNodeChanged and TreeNodeExpanded events when a node is selected
None Does not raise any event

The FillCustomers method accepts a parameter of type TreeNode. This node is nothing but the parent node on which the user has clicked. It is supplied from the TreeNodePopulate event handler. Once you finish configuring the new node, add it to the ChildNodes collection of the parent node.

Along similar lines, create a method called FillOrders(). You will use this method to populate OrderIDs for the selected customer. It looks like this:

private void FillOrders(TreeNode parent)
{
   DataSet ds = GetDataSet("select customerid,orderid 
   from orders where customerid='" + parent.Value + "'");
   foreach (DataRow row in ds.Tables[0].Rows)
   {
      TreeNode node         = new TreeNode();
      node.Text             = row["orderid"].ToString();
      node.Value            = row["orderid"].ToString();
      node.PopulateOnDemand = false;
      node.SelectAction     = TreeNodeSelectAction.SelectExpand;
      parent.ChildNodes.Add(node);
   }
}

Note that, because there is no further on-demand population, you set the PopulateOnDemand property to false.

Finally, add the TreeNodePopulate event handler as follows:

protected void TreeView1_TreeNodePopulate
(object sender, TreeNodeEventArgs e)
{
   switch (e.Node.Depth)
   {
      case 0:
         FillCustomers(e.Node);
         break;
      case 1:
         FillOrders(e.Node);
         break;
   }
}

The TreeNodePopulate event handler receives an event argument of type TreeNodeEventArgs. The TreeNodeEventArgs instance provides access to the node that is being expanded via its Node property. Depending on the nesting level, you need to call either the FillCustomers method (if it is a root node) or the FillOrders method (if it is a specific customer node). You do this checking by using the Depth property of the TreeNode class. According to the depth, you call corresponding methods.

Figure 5 shows a sample run of the Web form.

[Figure5.JPG]

Figure 5: Adding TreeNodes Via Code

Note how all the customer nodes indicate that they are expandable (+), even though they do not contain any child nodes initially. This behavior is because of the PopulateOnDemand property you learned about earlier.

Data Binding with XML File

Just like TreeView, XML documents also represent data in a hierarchical fashion. Expecting a link between TreeView and XML data is quite natural. In fact, with the help of the XmlDataSource control, you can actually data bind your TreeView with an XML document. The final example illustrates how you can do this.

Add one more Web form to the Web site. Drag and drop a TreeView, XmlDataSource, and Label on the Web form.

Right-click on your Web site in Solution Explorer and select "Add New Item...". A dialog like the one in Figure 6 will display.

[Figure6.JPG]

Figure 6: Add New Items Dialog

Select XML File and name it Books.xml. Add the following markup to the Books.xml file:

<?xml version="1.0" encoding="utf-8" ?>
<books>
   <category name="Web Development">
      <subcategory name="ASP.NET" url="~/example2.aspx?id=1">
      </subcategory>
      <subcategory name="Web Services" url="~/example2.aspx?id=2">
      </subcategory>
      <subcategory name="JSP" url="~/example2.aspx?id=3">
      </subcategory>
   </category>
   <category name="Windows Development">
      <subcategory name="Windows Forms" url="~/example2.aspx?id=4">
      </subcategory>
      <subcategory name="ActiveX" url="~/example2.aspx?id=5">
      </subcategory>
      <subcategory name="Smart Client" url="~/example2.aspx?id=6">
      </subcategory>
   </category>
   <category name="Component Development">
      <subcategory name="COM" url="~/example2.aspx?id=7">
      </subcategory>
      <subcategory name="DCOM" url="~/example2.aspx?id=8">
      </subcategory>
      <subcategory name="Remoting" url="~/example2.aspx?id=9">
      </subcategory>
   </category>
</books>

The root node is <books> and it contains <category> nodes. The <category> nodes further contain <subcategory> nodes. The <category> tag has an attribute, called name, that represents the name of the book category. Similarly, the <subcategory> tag has an attribute, called name, that represents sub-category name. Additionally, it has an attribute, called url, that points to the Web page where the user is supposed to be navigated. Note that the name attribute corresponds to the Text property and the url attribute corresponds to the NavigateUrl property that you used previously.

Now, open the smart tag of the XmlDataSource control and choose "Configure Data Source...". This will open the dialog in Figure 7.

[Figure7.JPG]

Figure 7: Configure Data Source Dialog

Set the DataFile property by entering the name of your XML file. Thus, books.xml will provide data to the XmlDataSource control, which in turn will be supplied to the TreeView.

Use ASP.NET 2.0's TreeView to Display Hierarchical Data

Next, open the smart tag of the TreeView control and set Data Source to XmlDataSource1 (see Figure 8).

[Figure8.JPG]

Figure 8: Smart Tag of TreeView After Selecting Data Source

Then, select the "Edit TreeNode Databindings..." option to open the TreeView DataBindings Editor (as shown in Figure 9). This dialog allows you to establish data bindings between the XmlDataSource control and the TreeView control.

[Figure9.JPG]

Figure 9: TreeView Databindings Editor

Under available data bindings, the dialog will automatically display the nesting of the XML file. Select "category" and click the Add button. To set the data binding, select "categoty" under the Selected data bindings list and set the TextField property to name. Note that name is an attribute of the <category> tag. By doing so, you indicate that you want to display the value of the name attribute of the <category> tag in the node. Similarly, select subcategory and set TextField and NavigateUrlField properties to name and url, respectively.

The above operations generate the following mark up for you:

<asp:TreeView ID="TreeView1" runat="server"
              DataSourceID="XmlDataSource1">
   <DataBindings>
      <asp:TreeNodeBinding DataMember="category"
                           TextField="name" />
      <asp:TreeNodeBinding DataMember="subcategory"
                           TextField="name" NavigateUrlField="url" />
   </DataBindings>
</asp:TreeView>

Note how each data binding that you established in the TreeView Databindings Editor is represented by a <asp:TreeNodeBinding> markup tag.

Finally, write some code in the Page_Load event of the Web form and SelectedNodeChanged event of the TreeView to display a message when a user selects a node (as you did in the first example). The following code shows these event handlers (they are very much the same as you wrote in the first example):

protected void Page_Load(object sender, EventArgs e)
{
   switch (Request.QueryString["id"])
   {
      case "1":
         Label1.Text = "You selected ASP.NET category";
         break;
      case "2":
         Label1.Text = "You selected Web Services category";
         break;
      case "3":
         Label1.Text = "You selected JSP category";
         break;
      case "4":
         Label1.Text = "You selected Windows Forms category";
         break;
      case "5":
         Label1.Text = "You selected ActiveX category";
         break;
      case "6":
         Label1.Text = "You selected Smart Client category";
         break;
      case "7":
         Label1.Text = "You selected COM category";
         break;
      case "8":
         Label1.Text = "You selected DCOM category";
         break;
      case "9":
         Label1.Text = "You selected Remoting category";
         break;
   }

}
protected void TreeView1_SelectedNodeChanged
(object sender, EventArgs e)
{
   Label1.Text = "You Selected " + 
   TreeView1.SelectedNode.Text + " category";
}

A sample run of the Web form will look exactly like Figure 3, except this time the TreeView is populated from the XML file.

A Neat, Flexible Way to Display Hierarchical Data

A professional-looking navigation system is a must for any Web site. The ASP.NET 2.0 TreeView control provides a neat and flexible way to display hierarchical data. The TreeView can be populated statically at design time or dynamically at runtime. You also can establish data binding between a TreeView and an XML file via the XmlDataSource control.

About the Author

Bipin Joshi is the founder and owner of BinaryIntellect Consulting, where he conducts professional training programs on .NET technologies. He is the author of Developer's Guide to ASP.NET 2.0 and co-author of three WROX press books on .NET 1.x. He also is a Microsoft MVP, member of ASPInsiders, MCAD, and MCT.



About the Author

Bipin Joshi

Bipin Joshi is a blogger and writes about apparently unrelated topics - Yoga & technology! A former Software Consultant by profession, Bipin has been programming since 1995 and has been working with the .NET framework ever since its inception. He has authored or co-authored half a dozen books and numerous articles on .NET technologies. He has also penned a few books on Yoga. He was a well known technology author, trainer and an active member of Microsoft developer community before he decided to take a backseat from the mainstream IT circle and dedicate himself completely to spiritual path. Having embraced Yoga way of life he now codes for fun and writes on his blogs. He can also be reached there.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Not long ago, security was viewed as one of the biggest obstacles to widespread adoption of cloud-based deployments for enterprise software solutions. However, the combination of advancing technology and an increasing variety of threats that companies must guard against is rapidly turning the tide. Cloud vendors typically offer a much higher level of data center and virtual system security than most organizations can or will build out on their own. Read this white paper to learn the five ways that cloud …

  • Live Event Date: September 16, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you starting an on-premise-to-cloud data migration project? Have you thought about how much space you might need for your online platform or how to handle data that might be related to users who no longer exist? If these questions or any other concerns have been plaguing you about your migration project, check out this eSeminar. Join our speakers Betsy Bilhorn, VP, Product Management at Scribe, Mike Virnig, PowerSucess Manager and Michele …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds