The DataGrid control, as you likely know, can easily be configured to add a column of buttons. By adding a ButtonColumn, whenever
one of the DataGrid's buttons in the column is clicked, the Web page posts back and the DataGrid's
ItemCommand
event fires. In fact, the same behavior can be noted if you manually add a Button or LinkButton control into a DataGrid
TemplateColumn, as the ButtonColumn class simply adds a Button (or LinkButton) Web control to each row of the DataGrid.
When the Button (or LinkButton) is clicked, its Command
event is raised. But how does the DataGrid know when
this event has been raised so that it can raise its ItemCommand
event in response? The answer is through a
process referred to as event bubbling. Event bubbling is the process of moving an event up the control hierarchy,
from a control low in the hierarchy - such as a Button within the row of a DataGrid - and percolating it up to an ancestor
control - such as the DataGrid. Once the ancestor control has learned of the event it can respond however it sees fit; in
the DataGrid's case, the DataGrid "swallows" the Button's Command
event (that is, it stops the bubbling) and
raises its own ItemCommand
event in response.
In this article we'll look at how, precisely, event bubbling works in ASP.NET. Event bubbling is a technique that all
server control developers should be aware of. Additionally, it can be used as a means to pass event information from a
User Control to its parent page, as discussed in Handle
Events from Web User Controls (although personally I find it simpler to just use the technique of having the User Control
raise its own events through the standard event firing syntax as discussed at
An Extensive
Examination of User Controls). Read on to learn more!
Understanding the Control Hierarchy
All ASP.NET pages are represented in code as a hierarchy of controls. While ASP.NET page developers are accustomed to working
in terms of the Visual Studio .NET designer and HTML and Web control syntax, this markup is automatically converted into a
programmatically-created control hierarchy when an ASP.NET page is first visited (or first visited after a change to its
.aspx
page's content). To better understand this process, let me refer to a previous article of mine,Understanding ASP.NET View State:
All ASP.NET server controls can have a parent control, along with a variable number of child controls. TheSystem.Web.UI.Page
class is derived from the base control class (System.Web.UI.Control
), and
therefore also can have a set of child controls. The top-level controls declared in an ASP.NET Web page's HTML portion are
the direct children of the autogeneratedPage
class. Web controls can also be nested inside one another. For
example, most ASP.NET Web pages contain a single server-side Web Form, with multiple Web controls inside the Web Form. The
Web Form is an HTML control (System.Web.UI.HtmlControls.HtmlForm
). Those Web controls inside the Web Form are
children of the Web Form.
Since server controls can have children, and each of their children may have children, and so on, a control and its
descendents form a tree of controls. This tree of controls is called the control hierarchy. The root of the control
hierarchy for an ASP.NET Web page is thePage
-derived class that is autogenerated by the ASP.NET engine.
Whew! Those last few paragraphs may have been a bit confusing, as this is not the easiest subject to discuss or digest. To
clear out any potential confusion, let's look at a quick example. Imagine you have an ASP.NET Web page with the following HTML
portion:
<html>
<body>
<h1>Welcome to my Homepage!</h1>
<form runat="server">
What is your name?
<asp:TextBox runat="server" ID="txtName"></asp:TextBox>
<br />What is your gender?
<asp:DropDownList runat="server" ID="ddlGender">
<asp:ListItem Select="True" Value="M">Male</asp:ListItem>
<asp:ListItem Value="F">Female</asp:ListItem>
<asp:ListItem Value="U">Undecided</asp:ListItem>
</asp:DropDownList>
<br />
<asp:Button runat="server" Text="Submit!"></asp:Button>
</form>
</body>
</html>
When this page is first visited, a class will be autogenerated that contains code to programmatically build up the control
hierarchy. The control hierarchy for this example [is shown below:]
This concept of a control hierarchy works just the same with a DataGrid. Specifically, a DataGrid contains a child control
for each of its rows. Each of these row controls contains a child for each of its columns. And each column control may
contain further controls. For ButtonColumns, the column control would contain a single child control - either a Button or
LinkButton control. For a TemplateColumn, the column control would contain its own hierarchy of children that composed the
HTML markup and Web controls specified in the template.
For example, imagine we had a DataGrid with two columns, a ButtonColumn and a BoundColumn. When binding the DataGrid to
a DataSource with three records, the DataGrid's control hierarchy might look like the following:
Bubbling Basics
When one of the DataGrid's Buttons are clicked, the Button's Command
event is fired. When this happens we
also want to have the DataGrid's ItemCommand
event fire, but as the above figure illustrates, there is a bit
of distance between the Button instance that was clicked and the DataGrid control. Event bubbling is employed to percolate
the event from the Button control up to the DataGrid, where the ItemCommand
event can be raised in response.
Event bubbling works using two protected methods of the Control
class:
RaiseBubbleEvent(source, eventArgs)
- when called, a specified event's information is
bubbled up to control's parent. The source parameter is typically a reference to the object doing the bubbling (the
actual Button control in the DataGrid example), whereas theeventArgs
contains information about the event
being bubbled (in the DataGrid example, the Button'sCommandEventArgs
information is percolated up, which includes
the Button'sCommandName
andCommandArgument
parameters).OnBubbleEvent(source, eventArgs)
- when an event is bubbled up to a control, the control'sOnBubbleEvent()
method fires. A control in the hierarchy may override this method to do some custom
processing on the bubbled event. In the DataGrid's case, theOnBubbleEvent()
method checks to see if
aCommandEventArgs
instance is being bubbled up. If so, it raises its ownItemCommand
event.
(This is a slight simplification of how things really work with the DataGrid.)
TheOnBubbleEvent()
method returns a Boolean value that indicates if bubbling of the event should continue.
IfOnBubbleEvent()
returns False, the event continues to bubble up the hierarchy; if it returns True, the
bubbling of that event ends. By default, a control'sOnBubbleEvent()
returns False, thereby allowing event
bubbling to progress unimpeded.
To demonstrate event bubbling, image that the Button in the third
DataGrid row is clicked. Clicking the Button causes a postback and raises the appropriate Button control's Click
event handler. The Button (and LinkButton) Web control's are coded such that when their Command
event is fired,
the event details are bubbled up the hierarchy. Specifically, the OnCommand()
method in the Button and LinkButton
classes looks as follows (comments added by yours truly):
|
This event is then bubbled up to the ButtonColumn, calling its OnBubbleEvent()
method, which, by default, does
nothing, thereby letting the bubbling continue. Following that, the DataGridItem's OnBubbleEvent()
method is
called. The DataGridItem's OnBubbleEvent()
checks to see if a CommandEventArgs
object is being bubbled
up and, if so, it crafts an instance of the DataGridCommandEventArgs
object based on the passed-inCommandEventArgs
object. This new type is then bubbled up to its parent (the DataGrid) and event bubbling is
ceased. Here's the code from the DataGridItem's OnBubbleEvent()
method:
|
Finally, in the DataGrid's OnBubbleEvent()
method a check is made to see if a DataGridCommandEventArgs
object has been bubbled up. If so, it halts the bubbling and raises its ItemCommand
event. (Note that the
DataGrid's OnBubbleEvent()
method is a bit more complex than the DataGridItem's because the DataGrid
might raise a number of events based on the CommandName
value in the DataGridCommandEventArgs
object.
(For example, a CommandName
of Edit raises the EditCommand
event, a CommandName
of Update
raises the UpdateCommand
event, and so on. Of course, regardless of the CommandName
the ItemCommand
always fires.)
The following figure depicts the chain of events just described:
Conclusion
In this article we saw how event bubbling is used to percolate events up the control hierarchy. This technique is an
invaluable one for control developers creating composite controls whose contents cannot be determined until runtime. The most
common Web control that fits into this class is the DataGrid, although both the DataList and Repeater use event bubbling in
a similar manner. For more information on creating these types of controls, along with a further discussion on event bubbling,
be sure to read Building
DataBound Templated Custom ASP.NET Server Controls.