This post describes creation of an ASP.NET control for creating collapsible nodes within GridViews, incorporating JavaScript for expanding and collapsing child nodes, and implementing custom event handling for node selection.
Introduction
I recently started to create an ASP.NET control that had collapsible nodes. The GridView
s made a visual tree looking control based on the parent to child business object.
For example, a main node would expand to show a sub-main node and then the sub-main node would have element nodes.
All of these controls had GridView
s that contained JavaScript to expand or collapse the child nodes. There were several ways that I found on the net to hook into the postback of the controls.
The code contains the main Node that I used for creating a collapse image background with the name of the node and then another GridView
control called MainTreeNode
that I set the datasource
and bind to during the OnRowDataBound
event of the GridView
.
<table style="border:solid 1px gray; height:390px;">
<tr>
<td valign="top">
<asp:GridView ID="MainGridview" runat="server"
AutoGenerateColumns="false"
CellPadding="0"
CellSpacing="0"
GridLines="None"
ShowFooter="false"
ShowHeader="false"
OnRowDataBound="MainGridview_OnRowDataBound">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<table>
<tr>
<td>
<a href="javascript:ExpandCollapse('div<%# DataBinder.Eval(
Container.DataItem, "ID")%>', 'selector<%# DataBinder.Eval(
Container.DataItem, "ID")%>');">
<div id="selector<%# DataBinder.Eval(Container.DataItem, "ID")%>"
class="expandBar">
I am able to FindControl
to get the MainTreeNode
and then set the datasource
to the DataItem
’s property array.
The main question at this point is how to get an OnClick
event to fire from the MainTreeNode
. Note the OnNodeSelectedEvent="MainTreeNode_OnNodeSelected"
of the child.
This node event was achieved by creating a control event and having the IPostBackEventHandler
interface implemented on the node.
public partial class MainTreeNode: GrideView, IPostBackEventHandler
{
public bool AllowPostBack { get; set; }
protected override void OnRowDataBound(GridViewRowEventArgs e){
base.OnRowDataBound(e);
if(e.Row.DataItem != null && AllowPostBack){
string arg = string.Format("argument_{0}", e.Row.RowIndex);
e.Row.Attributes.Add("onclick",
Page.ClientScript.GetPostBackEventReference(this, arg));
}
}
protected override void PrepareControlHierarchy(){
base.PrepareControlHierarchy();
}
protected override void RaisePostBackEvent(string eventArgument){
base.RaisePostBackEvent(eventArgument);
}
}
Simplistic of course, but as noted above, there are several areas of the control that can be utilized. This would be the basic functionality of causing a postback on the clicking of the node (GridRow
).
The next item to make sure the control has handled is the custom event handler in the MainTreeNode
class:
protected override void RaisePostbackEvent(string argument){
if (argument != null){
NodeSelectedEventArg e = new NodeSelectedEventArg();
OnNodeSelected(e);
}
}
private static readonly object NodeSelectedEventKey = new object();
public event EventHandler<nodeselectedeventarg> NodeSelectedEvent{
add { Events.AddHandler(NodeSelectedEventKey, value); }
remove { Events.RemoveHandler(NodeSelectedEventKey, value); }
}
protected virtual void OnNodeSelected(NodeSelectedEventArg e){
base.RaiseEvent(NodeSelectedEventKey, e);
}
protected virtual void NodeSelected(object DataItem){
NodeSelectedEventArg e = new NodeSelectedEventArg();
e.dataItem = DataItem;
OnNodeSelected(e);
}
The event is now hooked into the control’s Events
collection and can be used if the containing control subscribes to the event. A delegate could also work here as well.
Now the code is provided as the building block foundation, more details will depend on the work that needs to be accomplished by the event. The event argument class can be created to handle any data item that needs to be persistent between controls, and I have found it very useful to keep the data item intact through custom event arguments.
My previous post had taken the original postback requirement into account, but as provided in the above code, moving the GetPostBackEventReference
method into OnRowDataBound
gives the option to have the GridView.Row.DataItem
available for keeping track of any data in the data item for the postback.
Hopefully, this will clear up any questions on the previous posting. I have worked with this control for a bit, and I am very pleased with how the GridView
reacted to the upgrade… :)