Adding Client script to TreeNode Click Event

Most of the ASP.NET 2.0 controls provide way to add client events to them. For example, TextBoxes, checkbox are simply rendered as <input> elements and can be easily hooked to JavaScript functions either programmatically or declaratively.

However, TreeNode is a special case in that. There is no straightforward way to hookup client events to it. This is simply because they are rendered as Anchor <a> elements and its href and onClick attributes are itself consumed by ASP.NET framework.

However, with a little trick, the TreeNode element can be easily hooked up to client events.

The key to this solution is TreeNode’s PreRenderText and PostRenderText methods. The framework describes it as :

RenderPostText: Allows control developers to add additional rendering to the node.

RenderPreText: Allows control developers to add additional rendering to the node.

Using these methods, we can inject a simple <div> tag and enclose our Nodes(<a>) in those. For example:

 public class CTreeNode : TreeNode
 {
     protected override void RenderPreText(HtmlTextWriter writer)
     {
  
         writer.Write("<div onclick='CheckUnsavedStatus(this);'>");
         base.RenderPreText(writer);
     }
  
     public CTreeNode(string label, string value)
     {
         this.Text = label;
         this.Value = value;
  
     }
  
     protected override void RenderPostText(HtmlTextWriter writer)
     {
         writer.Write("</div>");
         base.RenderPostText(writer);
     }
  
 }

The code above will call the CheckUnsavedStatus client function passing it the reference of <div> element. Using this <div> element, we can alter our Node(<a> element) at the client side.

For example, We might want to call the server side event SelectedNodeChanged, only if user has saved its changes. The client script for this kind of scenario would be:

 function CheckUnsavedStatus(divObj)
  {
                 var HiddenSaveDataCheckObj=document.getElementById('<%=hiddenSaveDataCheck.ClientID %>');
                 if(HiddenSaveDataCheckObj==null)
                     return;
                 if(HiddenSaveDataCheckObj.value=='1')
                 {
                     var response=confirm('There are pending changes. Click Ok to continue and lose the changes');
                     if(response==false)
                     {
                          divObj.children[0].href='#';
                          return true;
                     }
                         
                 }    
 }

The function above changes the target link of node to # which suppresses the server side call and subsequently the postback. Here, hiddenSaveDataCheck is a HiddenField which tracks the save/unsaved status.

Note that:

To preserve the custom script generation on postback, the CreateNode method of TreeView control must be overrideen to return our CustomNode rather than default TreeNode implementation. This is easy:

 public class CustomTreeView:TreeView
     {
         protected override TreeNode CreateNode()
         {
             return new CTreeNode();
         }
     }

Another important thing to be kept in mind is SelectAction property of TreeNode. This property determines, what happens when you click a TreeNode. This is an enumeration described as :

TreeNodeSelectAction.Expand : Toggles the node between expanded and collapsed. Raises the TreeNodeExpanded event or the TreeNodeCollapsed event as appropriate.

TreeNodeSelectAction.None: Raises no events when a node is selected.

TreeNodeSelectAction.Select: Raises the SelectedNodeChanged event when a node is selected.

TreeNodeSelectAction.SelectExpand: Raises both the SelectedNodeChanged and the TreeNodeExpanded events when a node is selected. Nodes are only expanded, never collapsed.

This solution will work for all the cases irrespective of the setting, however it hardly makes it any sense to set it as TreeNodeSelectAction.None, since in this case, node is not rendered as hyperlink. For only client event, it should  preferably be set to TreeNodeSelectAction.Select. Since this also causes  a server side event SelectedNodeChanged, we can suppress the postback in our client function as discussed above.