Introduction
This article demonstrates how to use the free TreeView
control from Microsoft with C# and ASP.NET and how to intercept the server-side events on the client's browser to reduce the number of post-backs and increase performance of the application by handling the major events on the client.
Please provide a rating / vote before leaving this article
This is the only way authors get any type of credit for their work they freely share with everyone. It's sad to see articles that have helped over 100,000 people and fewer than 200 bother to vote or provide a rating.
Background
One common control used in web development is the TreeView
. Developers can choose to �roll their own� by using DHTML, purchase a third party control or use the free TreeView
provided by Microsoft for use within ASP.NET. This article focuses on leveraging the free TreeView
control made available by Microsoft.
One issue in using Microsoft�s free control is the lack of support and the fact that the control is designed to leverage ASP.NET�s server-side eventing architecture. Experienced web developers strive to reduce or eliminate as many server round trips as possible (commonly known as post-backs) to increase the end user�s satisfaction and response times.
This article will demonstrate how to use Microsoft�s TreeView
control in such a way as to intercept the typical server-side events on the client without losing functionality. The examples in this article are basic and demonstrate how to store �meta-data� with each node of the tree view that can be obtained and used on the client.
The control used in this article is a free download from Microsoft, and the control's overview is included in their online documentation.
What is the Microsoft TreeView?
The tree view provides an object oriented hierarchical or parent/child relationship view of data and meta-data. The most common example of a tree view is Windows Explorer�s directory structure where disk drives contain folders, folders contain sub-folders and folders contain files.
The MS TreeView
control exposes several classes you can use to dynamically build the tree. The two main classes you will use are the TreeView
and the TreeNode
classes. The TreeView
class provides the container that will hold 1 - N TreeNode
s. The tree nodes represent individual items in the parent/child structure. Below is a short list of the two classes' characteristics:
Tree View / Node Characteristics |
The TreeView control is the container for displaying the parent/child relationships. |
The TreeView control contains 1 � N tree nodes. |
A tree node represents an element in the parent/child relationship. |
A tree node can be the parent and/or child of other tree nodes. |
Tree nodes that have child nodes can be expanded to display their child nodes. |
Tree nodes that have child nodes can be collapsed to hide their child nodes. |
Tree nodes have a property named NodeData that can be used to store meta-data. |
Tree nodes can display images (think of Windows Explorer where an image of either a closed or open folder is displayed). |
Tree nodes can be hyperlinks to other web pages or sites. |
What is the NodeData property?
The TreeNode
class provides a property named NodeData
. This property is a great place to store meta-data you may need to access either on the client or on the server.
The issue is the property�s underlying type is a string. So how do you store several pieces of meta-data in the property? Well, I�ve found the easiest way to do this is to create a delimited list of key valued pairs that can be accessed on either the client or server. I typically use a semi-colon (;) as the main delimiter and separate the key and value with an equal sign (=). For example, I would store meta-data in a tree node�s NodeData
property as follows:
TreeNode tn = new TreeNode();
tn.Text = "Root Parent Node";
tn.NodeData = "Id=1000;Name=Mike Elliott;Article=ASP.NET " +
"Tree View Control & the Client's Browser";
You can get at the NodeData
property�s information on the client by accessing the element's nodeData
attribute. This will allow you to obtain detail information about the tree node which the user has selected and display or submit the information as necessary. This single piece of functionality can reduce the majority of round trips or post-backs generally caused by needing meta-data associated with the node selected by the end user (this will be demonstrated later in the article).
You could choose to use XML instead of a key valued pair, but for performance reasons, I prefer to work with key valued pairs because they are stored in arrays.
Preparing to build the examples
Before jumping into examples, I want to provide a little set-up information in case you want to follow along.
The first step is to download Microsoft�s free control suite. The control suite contains more than just the TreeView
; however, I am not going to cover the Tab
control in this article. Once downloaded from the URL listed earlier in this article, you will have to build or run the build.bat file to produce the Microsoft.Web.UI.WebControls
assembly by following the instructions in the readme file obtained in the download.
Be prepared, the .bat file distributed with the free download has a bug that may prevent the .dll from being produced (most of the time), and there are no warnings or exception messages given.
Open the build.bat file in Notepad, and find the line that reads:
csc.exe /out:build\Microsoft.Web.UI.WebControls.dll @IEWebControls.rsp
Unless you have placed the csc.exe compile utility in your main path or placed a copy of the executable in the system or system32 directory, the .bat file will not find the compile utility and fails silently.
To correct this, modify the .bat file to have the full path to the csc.exe compile utility as shown below (the location and version may be different on your machine):
C:\Windows\Microsoft.NET\Framework\v1.14322\csc.exe
/out:build\Microsoft.Web.UI.WebControls.dll @IEWebControls.rsp
This should correct the build issue and allow the Microsoft.Web.UI.WebControls.dll to be produced as the readme indicates in the download. Complete the installation instructions as detailed in the readme file.
Once you have properly installed the control library, create a new ASP.NET web application and add a reference to the newly created Microsoft.Web.UI.WebControls.dll. Next, add a Web Form named TVExample.aspx to the new web project. Now you will be ready to copy and paste the code as it is described in the article.
Client side events
I am going to demonstrate how to intercept the following events on the client instead of allowing server-side post-backs.
Event |
Description |
onSelectedIndexChange |
The event fired when the end user clicks a node in the tree view. |
onExpand |
The event fired when the end user expands a node. |
onCollapse |
The event fired when the end user collapses a node. |
onDblClick |
The event fired when the end user double clicks a node. |
onContextMenu |
The event fired when the end user right clicks a node in the tree view.
You will have to suppress the typical right-click context menu from being displayed and replace it with your functionality. |
These are the most common events you will need to intercept to prevent the intrinsic server-side events or post-backs from happening. This should greatly increase your end user�s experience and increase the response time. It will also reduce the load on your web servers by pushing much of the work to the client�s machine.
The following examples are going to display the intercepted event, how to get the meta-data from the tree node's NodeData
property (on the client) and how to parse the key valued pair information from the NodeData
property.
Getting to the code
One point I want to make before getting into the code is ASP.NET provides access to most HTML and JavaScript attributes through the IAttributeAccessor
interface. HtmlControl
, ListItem
, UserControl
and WebControl
types include intrinsic implementation for this interface. This interface allows programmatic access to any attribute defined in the tag of a server control. This is a great feature that allows you to intercept server-side events by pointing the events to your own JavaScript functions as you will see later in the article.
The HTML and the JavaScript code we will use to intercept what are typically server-side events is pretty straightforward and should be self-documenting, but here are a few high points to which you should pay attention:
- We register the
Microsoft.Web.UI.WebControls
within the HTML page. This gives us declarative access to create the tree view: @Register TagPrefix=�iewc�
Namespace=�Microsoft.Web.UI.WebControls�
Assembly=�Microsoft.Web.UI.WebControls�
- There is a JavaScript function that will be called for each event we intercept on the client:
function TVIndexChanged()
{
ChangeText( 'node changed' );
}
function TVNodeExpand()
{
ChangeText( 'onexpand' );
}
function TVNodeCollapse()
{
ChangeText( 'oncollapse' );
}
function TVDoubleClick()
{
ChangeText( 'dblclick' );
}
function TVRightClick()
{
var tree = GetTreeHandle();
var treenode;
if ( null == tree || undefined == tree )
return;
treenode = tree.getTreeNode( event.treeNodeIndex );
if ( null == treenode || undefined == treenode )
return;
tree.selectedNodeIndex = event.treeNodeIndex;
ChangeText( 'oncontextmenu' );
}
- There is a JavaScript function that allows us to get a handle to the
TreeView
control itself:
function GetTreeHandle()
{
var tree;
var treeName = 'tvControl';
tree = document.getElementById( treeName );
if ( null == tree || undefined == tree )
return null;
return tree;
}
- There is a JavaScript function that allows us to get a handle to the tree node object the end user has selected:
function GetSelectedNode()
{
var tree = GetTreeHandle();
var treeNode;
if ( null == tree || undefined == tree )
return null;
treeNode = tree.getTreeNode( tree.selectedNodeIndex );
if ( null == treeNode || undefined == treeNode )
return null;
return treeNode;
}
- There is a JavaScript function used to retrieve the
NodeData
attribute's information and return the value of the key passed in. This is one of the most important functions that allows you to retrieve the meta-data on the client and use it as necessary:
function GetKeyValue( searchKey )
{
var treenode = GetSelectedNode();
if ( null == treenode || undefined == treenode )
return null;
var nodeDataAry = treenode.getAttribute( 'nodeData' );
if ( null == nodeDataAry || undefined == nodeDataAry )
return null;
nodeDataAry = nodeDataAry.split( ';' );
if ( null == nodeDataAry || undefined == nodeDataAry ||
0 >= nodeDataAry.length )
return null;
var count = 0;
var returnValue = null;
while ( count < nodeDataAry.length )
{
var workingItem = nodeDataAry[ count ];
if ( 0 >= workingItem.length )
{
count++;
continue;
}
var kv = workingItem.split( '=' );
if ( 1 >= kv.length )
{
count++;
continue;
}
var key = kv[ 0 ];
var kValue = kv[ 1 ];
if ( key != searchKey )
{
count++;
continue;
}
returnValue = kValue;
break;
}
return returnValue;
}
The entire TVExample.aspx code set
<%@ Page language="c#" Codebehind="TVExample.aspx.cs"
AutoEventWireup="false"
Inherits="Mike.Elliott.Articles.TreeView.TVExample" %>
<%@ Register TagPrefix="iewc"
Namespace="Microsoft.Web.UI.WebControls"
Assembly="Microsoft.Web.UI.WebControls" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Tree View Example</title>
<meta name="GENERATOR"
Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name=vs_defaultClientScript content="JavaScript">
<meta name=vs_targetSchema
content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body MS_POSITIONING="GridLayout">
<script language="javascript">
function TVIndexChanged()
{
ChangeText( 'node changed' );
}
function TVNodeExpand()
{
ChangeText( 'onexpand' );
}
function TVNodeCollapse()
{
ChangeText( 'oncollapse' );
}
function TVDoubleClick()
{
ChangeText( 'dblclick' );
}
function TVRightClick()
{
var tree = GetTreeHandle();
var treenode;
if ( null == tree || undefined == tree )
return;
treenode = tree.getTreeNode( event.treeNodeIndex );
if ( null == treenode || undefined == treenode )
return;
tree.selectedNodeIndex = event.treeNodeIndex;
ChangeText( 'oncontextmenu' );
}
function ChangeText( eventName )
{
var treeNode = GetSelectedNode();
if ( null == treeNode || undefined == treeNode )
{
return;
}
var nodeData =
treeNode.getAttribute( 'nodeData' ).split( ';' );
var id = GetKeyValue( 'SomeId' );
var name = GetKeyValue( 'Name' );
document.getElementById( 'txtEvent' ).value =
eventName;
document.getElementById( 'txtId' ).value = id;
document.getElementById( 'txtName' ).value = name;
}
function GetKeyValue( searchKey )
{
var treenode = GetSelectedNode();
if ( null == treenode || undefined == treenode )
return null;
var nodeDataAry = treenode.getAttribute( 'nodeData' );
if ( null == nodeDataAry || undefined == nodeDataAry )
return null;
nodeDataAry = nodeDataAry.split( ';' );
if ( null == nodeDataAry || undefined == nodeDataAry ||
0 >= nodeDataAry.length )
return null;
var count = 0;
var returnValue = null;
while ( count < nodeDataAry.length )
{
var workingItem = nodeDataAry[ count ];
if ( 0 >= workingItem.length )
{
count++;
continue;
}
var kv = workingItem.split( '=' );
if ( 1 >= kv.length )
{
count++;
continue;
}
var key = kv[ 0 ];
var kValue = kv[ 1 ];
if ( key != searchKey )
{
count++;
continue;
}
returnValue = kValue;
break;
}
return returnValue;
}
function GetTreeHandle()
{
var tree;
var treeName = 'tvControl';
tree = document.getElementById( treeName );
if ( null == tree || undefined == tree )
return null;
return tree;
}
function GetSelectedNode()
{
var tree = GetTreeHandle();
var treeNode;
if ( null == tree || undefined == tree )
return null;
treeNode = tree.getTreeNode( tree.selectedNodeIndex );
if ( null == treeNode || undefined == treeNode )
return null;
return treeNode;
}
</script>
<form id="frmTVExample" method="post" runat="server">
<table width="100%" align="center" border="0">
<tr><td>
<iewc:treeview id="tvControl" runat="server"
SystemImagesPath=
"/webctrl_client/1_0/treeimages/"
EnableViewState="False">
</iewc:treeview>
</td></tr>
<tr>
<td><input type="text" id="txtId" name="txtId">
</td></tr>
<tr>
<td><input type="text" id="txtName" name="txtName">
</td></tr>
<tr>
<td><INPUT type="text" id="txtEvent" name="txtEvent">
</td> </tr>
</table>
</form>
</body>
</html>
The TVExample.aspx.cs or code behind (this section does not include the OnInit
and InitializeComponent
methods ASP.NET creates for you, so do not delete these methods or their content. It also does not include all the using
directives, so don�t delete the using
directives created for you, but you should add the using Microsoft.Web.UI.WebControls
directive as indicated in the example):
using Microsoft.Web.UI.WebControls;
namespace Mike.Elliott.Articles.TreeView
{
public class TVExample : System.Web.UI.Page
{
protected Microsoft.Web.UI.WebControls.TreeView tvControl;
private void Page_Load( object sender, System.EventArgs e )
{
TreeNode root = new TreeNode();
root.Text = "Root Parent Node";
root.NodeData = "SomeId=1000;Name=Mike Elliott";
TreeNode tn = new TreeNode();
tn.Text = "Child 1 of Root Parent";
tn.NodeData = "SomeId=1001;Name=Play For Sport, Inc.";
root.Nodes.Add( tn );
tn = new TreeNode();
tn.Text = "Child 2 or Root Parent";
tn.NodeData = "SomeId=1002;Name=Chip Oxendine";
TreeNode cn = new TreeNode();
cn.Text = "Grandchild of Root Parent";
cn.NodeData = "SomeId=1003;Name=Mike Elliott";
tn.Nodes.Add( cn );
root.Nodes.Add( tn );
root.Expanded = true;
this.tvControl.Nodes.Add( root );
this.OverRideServerEvents();
}
private void OverRideServerEvents()
{
string clickHandler = "TVIndexChanged();";
this.tvControl.Attributes.Add( "onselectedindexchange",
clickHandler );
clickHandler = "TVNodeExpand();";
this.tvControl.Attributes.Add( "onexpand",
clickHandler );
clickHandler = "TVNodeCollapse();";
this.tvControl.Attributes.Add( "oncollapse",
clickHandler );
clickHandler = "TVDoubleClick();";
this.tvControl.Attributes.Add( "ondblclick",
clickHandler );
clickHandler = "TVRightClick();";
this.tvControl.Attributes.Add( "oncontextmenu",
clickHandler );
}
}
}
The Page_Load
callback, event or method is the method called when the page loads. In this method we are simply creating the tree nodes, providing the node�s text, giving the node�s NodeData
property meta-data in a key/value pair delimited string, adding the node to its parent and then finally adding the root node to the tree view.
Next, the Page_Load
method delegates the work of wiring up the JavaScript intercept functions to the OverRideServerEvents
method. As mentioned earlier in this article, ASP.NET provides a mechanism that allows you to add attributes to your server controls. This method simply adds attributes to the TreeView
control for each event we want to intercept on the client. We add the attribute, which is the formal name of the tree view�s server-side event, and then redirect the event to our JavaScript function, and that�s all there is to it.
When you cause any of the events to fire, the events are intercepted by our JavaScript functions, the target tree node�s meta-data is parsed and displayed in the text boxes and the intercepted event�s name is also displayed so you can see exactly what's happening.
What's going on?
Step |
Description |
1 |
The page is loaded. |
2 |
The tree view is created. |
3 |
A tree node is created. |
4 |
The tree node's Test and NodeData properties are set. |
5 |
The tree node is added to its parent node's Node collection. |
6 |
The root tree node is added to the tree view's Node collection. |
Although I didn�t cover it in this article, you can completely manage the tree view�s state on the client by storing the state of each tree node (expanded/collapsed). This is something you will need to do to completely push the tree view�s functionality to the client. Otherwise, as soon as you cause a post-back, you will lose the state of which nodes were expanded, which nodes were collapsed and which node was selected. This is a topic that is a little more involved, and I will cover in a subsequent article.
Summary
It is very much possible to move the heavy server-side events to the client to streamline your ASP.NET applications that make use of the free MS TreeView
control. It is very straightforward, and the concepts can be carried over to most of the intrinsic server controls included with ASP.NET.
History