Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

CustomTreeView for standard SQL data

4.00/5 (4 votes)
5 Dec 2007CPOL5 min read 1   557  
An ASP.NET TreeView, compatible with standard DataViews, and fully supports declarative syntax.

Introduction

ASP.NET 1.0 did not come with a treeview control. I guess this is something many people needed because Microsoft has introduced such a control with ASP.NET 2. Still, there is something particular with MS's TreeView that makes it unpleasant to use when you work with "standard" data: this TreeView needs a data source that implements IHierarchicalEnumerable (for example: XML data..). When you want to bind this TreeView with SQL data, you must write some code (i.e., you cannot use declarative syntax, which is ASP.NET's landmark functionality). The CustomTreeView presented here gives the possibility to display data from a standard DataView, and still benefit from ASP.NET's declarative syntax (through the template NodeTemplate).

Background

The CustomTreeView is a templated data-bound control. I will not explain how this works since you'll find the full details on this in MSDN (or in my former article NestedRepeater).

Using the code

Data source

I'll display data from a table called GEOGRAPHY. This table needs a Primary Key / Foreign Key relationship. This is the relationship that creates the hierarchical structure rendered by the treeview. Here, GEO_PARENT references GEO_ID to create father-child relations.

Screenshot - ASP.NET_TreeView0.JPG

Code behind

The data from GEOGRAPHY is stored into a DataView. A DataRelation is created within the DataView, then the filter RowFilter selects the topmost nodes. Here is the code:

C#
SqlDataAdapter da = new SqlDataAdapter("select * from GEOGRAPHY",
            ConfigurationManager.ConnectionStrings[1].ConnectionString);

DataSet ds = new DataSet();
da.Fill(ds, "geo");
ds.Relations.Add("FatherChild", ds.Tables["geo"].Columns["GEO_ID"],
    ds.Tables["geo"].Columns["GEO_PARENT"]);

myTree.DataSource = ds.Tables["geo"].DefaultView;
myTree.RowFilter = "GEO_PARENT is null";
myTree.RelationName = "FatherChild";

myTree.DataBind();

Note that the DataRelation is given a name, and this name is supplied to the CustomTreeView through the property RelationName.

On the Webform

In this example, I'll display a tree with countries, regions, cities... and some illustrative functionalities with links and a button. To use the CustomTreeView on your webform, you must first register the assembly:

ASP.NET
<%@ Register tagprefix="WCC" 
    Namespace="WebCustomControls" Assembly="WebControls"%>

If you use Visual Studio, you must add a reference to "WebControls.dll" in your project for this line to work properly. And then:

XML
<WCC:CustomTreeView id=myTree runat="server">
  <NodeTemplate>
    <%# (Container.DataItem as DataRow)["GEO_NAME"]%>
        <%# String.Format("<a href=\"http://www.google.com/search?q={0}\" 
             target=\"_blank\">Google</a>",
                 (Container.DataItem as DataRowView)[1])%>
    <asp:ImageButton runat=server ID=BnSend 
       ImageUrl="~/IMGs/Valid.gif" 
       CommandArgument=<%# (Container.DataItem as DataRowView)[0] %> 
       ImageAlign=AbsMiddle 
       OnCommand=DoValid />
  </NodeTemplate>
</WCC:/NodeTemplate>

With the NodeTemplate class, you can add whatever control for each node. This template will be instantiated for each node.

In order to expand/close the node, you must click the "folder" images (see below for an example). This leaves you the possibility to use the "Click" event on the node's content.

Images

To keep things simple here, the URL for the images is hard-coded. All the images are located in a directory "IMGs" placed under the website's root folder. You can display your own images by supplying the appropriate images (but keeping the names that are hard-coded) in the /IMGs folder. But be sure to keep the "menu_bar_invisible.gif" image as it is. If you decide to use your own images, note that all the images must have identical dimensions. You also may add some extra properties in order to allow the host webform to dynamically specify the URLs. When you add some controls into the NodeTemplate, be sure that the images used to render the tree's branches are big enough, otherwise margins will appear between these images, thus "breaking" the tree, as shown below:

Screenshot - ASP.NET_TreeView1.JPG

In this example, the image's height is just 16 pixels, while the button needs at least 24 pixels. Here, you have to supply your own larger images.

How it works

A few things need some explanation.

m_lstColumnNeedVisualRendering

We use this list in order to know where we must display "menu_bar.gif" and where we must display "menu_bar_invisible.gif". These are the two images used to visually render the tree (the tree's branches). The difficulty comes from the fact that there must be no bar once we have reached the last child of a node, even if this last child has sub-children.

Screenshot - ASP.NET_TreeView2.JPG

In the example above, the vertical bar under "Europe" stops when we reach "France", even if the sub-nodes under France are also in Europe. m_lstColumnNeedVisualRendering is a list of booleans, one boolean for each column in the tree structure. If the element at position i in the list is true, then a bar is displayed for column i, otherwise an invisible GIF is "displayed".

m_listOpenedNodes

This list allows us to know which nodes were expanded when the user submitted the form that hosts the CustomTreeView. This way, we can render the CustomTreeView in exactly the same state as it was on the client's browser. On the generated HTML, this list is stored inside a hidden field (<input type=hidden...>). When the user expands a node, the JavaScript client code adds this node to the list (and removes it, if the user subsequently closes the node). On postback, the content of this hidden field is read by using the Request.Form collection. This is because the hidden field's viewstate is not handled by .NET (since it was dynamically added to the control).

JavaScript

Under each node, all child nodes are placed inside a SPAN. If the node's ID is i, then the SPAN's ID is 'Level_i'. The "folder" image for the node has an ID 'Image_i'. When the user clicks on a node, the JavaScript function ExpandNode will show/hide the adequate SPAN, and change the folder image to reflect the change.

Conclusion

So here's the output:

Screenshot - ASP.NET_TreeView3.JPG

With each node, a hyperlink has been added, to allow the user to display the Google page for this item. An ImageButton has also been added. When clicked, this button submits the webform. Once the Click event has been handled server-side, the tree is displayed in exactly the same state on the user's browser.

License

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