When the Button server control invokes an event handler registered for its Command event, it passes an instance of the CommandEventArgs event data class into it.. ❑ A property named Comm
Trang 1Event Bubbling and Button
Client Control This chapter discusses the implementation of the ASP.NET AJAX Button client control and Web pages that use this control You’ll also learn how to implement custom client controls that bubble their events up to their parent client controls, and how to implement custom client controls that catch the events that their child controls bubble up
CommandEventArgs
As you’ll see later in this chapter, the Button client control raises an event named command when the user clicks the button The ASP.NET AJAX CommandEventArgs class is the event data class for the command event, as defined in Listing 9-1
Listing 9-1: The CommandEventArgs Class
Sys.Preview.UI.CommandEventArgs =function Sys$Preview$UI$CommandEventArgs(commandName, argument){
Sys.Preview.UI.CommandEventArgs.initializeBase(this);
this._commandName = commandName;
this._argument = argument;
}function Sys$Preview$UI$CommandEventArgs$get_argument(){
return this._argument;
}function Sys$Preview$UI$CommandEventArgs$get_commandName(){
return this._commandName;
}
(continued)
Trang 2properties: [ {name: ‘argument’, type: String, readOnly: true},
{name: ‘commandName’, type: String, readOnly: true} ]
}
Sys.Preview.UI.CommandEventArgs.registerClass(‘Sys.Preview.UI.CommandEventArgs’,
Sys.EventArgs);
The CommandEventArgs class exposes two read-only properties of type string named commandName
and argument The constructor of this class takes two string parameters and assigns them to these two
The constructor of this class is the only way to set the values of the commandName and argument
properties The CommandEventArgs class comes with two methods named get_commandName and
get_argument that respectively return the values of the commandName and argument properties of the
CommandEventArgs
The get_commandName and get_argument methods are defined on the prototype property of the
CommandEventArgs class As such, they are considered instance methods and must be invoked on a
class instance
The CommandEventArgs class, like any other ASP.NET AJAX class, exposes a property named
descriptor that describes the members of the class An object literal with a single name/value pair is
assigned to the prototype property This name/value pair describes the commandName and argument
properties of the CommandEventArgs class
Every ASP.NET AJAX event data class must directly or indirectly inherit from the EventArgs base class
The CommandEventArgs class is no exception:
Sys.Preview.UI.CommandEventArgs.registerClass(‘Sys.Preview.UI.CommandEventArgs’,
Sys.EventArgs);
Trang 3Button Client Control
The ASP.NET Button server control exposes the following important features:
❑ An event named Command : When the end user clicks a Button server control, the control raises two events, Click and Command The event data class associated with the Command event is an ASP.NET class named CommandEventArgs When the Button server control invokes an event handler registered for its Command event, it passes an instance of the CommandEventArgs event data class into it
❑ A property named CommandName : The Button server control assigns the value of its Name property to the CommandName property of the CommandEventArgs object that it passes into the event handlers registered for its Command event
Command-❑ A property named CommandArgument : The Button server control optionally assigns the value of its CommandArgument property to the CommandArgument property of the CommandEventArgs object that it passes into the event handlers registered for its Command event
The ASP.NET AJAX Button client control emulates the ASP.NET Button server control to offer these three features on the client side, as discussed in the following sections
Constructor
As you can see in Listing 9-2 , the constructor of the Button client control takes a single argument that references the DOM element that the control represents The constructor calls the initializeBase method to invoke the constructor of its base class, passing in the reference to the DOM element The
Button client control is then registered as the subclass of the Control base class
Listing 9-2: The Constructor of the Button Client Control
Sys.Preview.UI.Button = function Sys$Preview$UI$Button(associatedElement){
Sys.Preview.UI.Button.initializeBase(this, [associatedElement]);
}Sys.Preview.UI.Button.registerClass(‘Sys.Preview.UI.Button’, Sys.UI.Control)
prototype
As Listing 9-3 shows, the Button client control exposes nine instance methods They are instance ods because they’re directly defined on the prototype property of the class As such, you must invoke these methods on a class instance
Trang 4Listing 9-3: The prototype Property of the Button Client Control
The Button client control exposes a property named argument , which emulates the CommandArgument
property of the Button server control As Listing 9-4 shows, the get_argument and set_argument
methods of the Button client control emulate the getter and setter of the Button server control’s
CommandArgument property
Note that the set_argument method calls the raisePropertyChanged method to raise the
propertyChanged event The Button client control inherits this method from its base class
Listing 9-4: The get_argument and set_argument Methods of the Button Client Control
As Listing 9-5 shows, the Button client control exposes a property named command and two methods
named get_command and set_command that emulate the CommandName property of the Button server
control and its associated getter and setter methods Again, note that the set_command method invokes
Trang 5the raisePropertyChanged method to raise the propertyChanged event and, consequently, to invoke all the event handlers registered for this event
Listing 9-5: The get_command and set_command Methods of the Button Client Control
function Sys$Preview$UI$Button$get_command(){
return this._command;
}function Sys$Preview$UI$Button$set_command(value){
if (this._command !== value) {
this._command = value;
this.raisePropertyChanged(‘command’);
} }
Sys.Preview.UI.Button.callBaseMethod(this, ‘initialize’);
this._clickHandler = Function.createDelegate(this, this._onClick);
$addHandler(this.get_element(), “click”, this._clickHandler);
}
The Button client control’s implementation of this method follows the same implementation pattern
as the initialize method of the HyperLink client control discussed in the previous chapter First, it calls the callBaseMethod method to invoke the initialize method of its base class Every time you implement a client component that overrides the initialize method, your component’s implementa-tion must always call the callBaseMethod method to invoke the initialize method of its base class
to allow the base class to initialize itself:
Sys.Preview.UI.Button.callBaseMethod(this, ‘initialize’);
Next, the initialize method of the Button client control calls the createDelegate method on the
Function class to create a delegate that represents the _onClick method of the Button control:
this._clickHandler = Function.createDelegate(this, this._onClick);
Finally, it calls the addHandler static method on the DomEvent class to register this delegate as an event handler for the click event of the DOM element that the Button client control represents This means that when the end user clicks the DOM element and raises its click event, it automatically invokes this delegate, which in turn invokes the method that it represents — the _onClick method
Trang 6add_click
Following the same implementation pattern as the HyperLink client control, the Button client control
exposes two methods named add_click and remove_click that allow you to add a specified handler
to and remove a specified handler from the list of handlers registered for the click event of the Button
client control, as shown in Listing 9-7
Listing 9-7: The add_click Method of the Button Client Control
One of the great things about the ASP.NET Button server control is that it bubbles its Command event up
to its parent server controls This plays a significant role in composite controls such as GridView and
DetailsView Thanks to event bubbling, these composite controls can catch the events raised by their
child controls, such as a Button server control, and expose them as top-level events This allows these
composite controls to hide their child controls from their clients and consequently act as a single entity
The _onClick method of the Button client control emulates the same feature in client-side
program-ming, as shown in Listing 9-8
Listing 9-8: The _ on Click Method of the Button Client Control
This method first calls the get_events method to return a reference to the EventHandlerList that
contains all the event handlers registered for the events that the Button client control exposes The
Button client control inherits the get_events method from its base class Next, the _onClick method
calls the getHandler method on the EventHandlerList to return a JavaScript function whose
invoca-tion automatically invokes all the event handlers registered for the click event of the Button control:
Trang 7var handler = this.get_events().getHandler(“click”);
Next, the _onClick method calls this JavaScript function to invoke all the event handlers registered for the click event:
handler(this, Sys.EventArgs.Empty);
So far, there was nothing special about the _onClick method What makes the _onClick method of the
Button client control very different from the _onClick method of client controls such as the HyperLink control is that the _onClick method creates an instance of the CommandEventArgs event data class (discussed in the previous section), passing in the values of the command and argument properties of the
Button client control:
var e = new Sys.Preview.UI.CommandEventArgs(this._command, this._arg);
Finally, the _onClick method calls the raiseBubbleEvent method, passing in the CommandEventArgs event data object to bubble the Button control’s command event up to its parent client control:
this.raiseBubbleEvent(this, e);
Keep in mind that every client control inherits the raiseBubbleEvent method from the Control class This method provides you with a very nice mechanism to bubble the events of your custom controls to their parent controls to allow the event handlers of the parent controls to handle these events This enables the parent of a child control to catch the events raised by its child controls and expose them as its own events This way, the clients of the parent control do not have to deal with the child controls
Instead, they register their event handlers for the events that the parent control exposes You’ll see an example of this later
dispose
As you can see in Listing 9-9 , the Button client control overrides the dispose method of its base class to remove all the event handlers registered for its click event Note that the Button control’s implementa-tion of this method calls the callBaseMethod method to invoke the dispose method of its base class to allow its base class to do its final cleanup before it is disposed of Your custom client control’s implemen-tation of the dispose method must always invoke the dispose method of its base class
Listing 9-9: The dispose Method of the Button Client Control
function Sys$Preview$UI$Button$dispose(){
if (this._clickHandler) $removeHandler(this.get_element(), “click”, this._clickHandler);
Sys.Preview.UI.Button.callBaseMethod(this, ‘dispose’);
}
Trang 8
descriptor
The Button client control, like any ASP.NET AJAX client class, exposes a property named descriptor
that describes the members of the Button control The value of this property is always an object literal
As Listing 9-10 shows, this object contains two name/value pairs, where the first name/value pair
describes the properties of the Button control, and the second name/value pair describes the events
that the control exposes The Button control exposes the command and argument properties and the
click event
Listing 9-10: The descriptor Property of the Button Client Control
Sys.Preview.UI.Button.descriptor =
{
properties: [ { name: ‘command’, type: String },
{ name: ‘argument’, type: String } ],
events: [ { name: ‘click’ } ]
}
Using Button Client Control
This section uses a couple of examples to show you the significance of the Button client control’s
event-bubbling capability Event bubbling involves two important methods of the Control base class:
onBubbleEvent and raiseBubbleEvent It is the responsibility of a child client control to invoke the
raiseBubbleEvent method to bubble its events to its parent client controls It is the responsibility of
the parent client control to override the onBubbleEvent method to catch and to optionally handle the
event bubbled up by its child client control
Catching a Bubbled Event
The _onClick method of the Button client control calls the raiseBubbleEvent method to bubble its
command event up to its parent client controls The first example shows you a parent client control
named GridView that overrides the onBubbleEvent method to catch the command event that its child
Button client controls bubble up
Listing 9-11 presents the GridView.js JavaScript file that contains the implementation of the GridView
Trang 9var handled = false;
if (args instanceof Sys.Preview.UI.CommandEventArgs) {
switch (args.get_commandName()) {
}CustomComponents.GridView.prototype ={
onBubbleEvent : CustomComponents$GridView$onBubbleEvent}
CustomComponents.GridView.registerClass(“CustomComponents.GridView”, Sys.UI.Control);
if(typeof(Sys)!==’undefined’) Sys.Application.notifyScriptLoaded();
As you can see, the GridView client control exposes a constructor and a method named onBubbleEvent that override the onBubbleEvent method of the Control base class
Constructor
Listing 9-12 shows the constructor of the GridView client control
Listing 9-12: The Constructor of the GridView Client Control
CustomComponents.GridView = function CustomComponents$GridView(associatedElement){
CustomComponents.GridView.initializeBase(this, [associatedElement]);
}CustomComponents.GridView.registerClass(“CustomComponents.GridView”, Sys.UI.Control);
As with any other ASP.NET AJAX client control, this constructor takes an argument that references the DOM element that the control represents It then calls the initializeBase method to invoke the constructor of its base class, passing in the reference to the DOM element
At the end of this listing, the GridView client control is registered as the subclass of the Control base class:
CustomComponents.GridView.registerClass(“CustomComponents.GridView”, Sys.UI.Control);
Trang 10
on BubbleEvent
The GridView client control overrides the onBubbleEvent method of its Control base class, as shown
in Listing 9-13 Pay close attention to the implementation pattern used to implement the onBubbleEvent
method, because the same pattern is used to implement the onBubbleEvent method of all parent client
controls that need to catch the events raised by their child client controls
Listing 9-13: The on BubbleEvent Method of the GridView Client Control
function CustomComponents$GridView$onBubbleEvent(source, args)
{
var handled = false;
if (args instanceof Sys.Preview.UI.CommandEventArgs)
As shown in this listing, you take the following steps to implement the onBubbleEvent method of a
parent client control:
1 Declare a local variable named handled and initialize its value to false :
var handled = false;
2 Use the instanceof operator to determine whether the event is of the type that the parent
client control handles In this case, the GridView client control handles only command events:
if (args instanceof Sys.Preview.UI.CommandEventArgs)
3 Call the get_commandName method on the second parameter passed into the onBubbleEvent
method to access the command name:
var commandName = args.get_commandName();
4 Use a switch statement that contains one branch for each command name that the parent client
control handles In this case the GridView client control handles only the Select and Delete
commands
5 Handle the event within each branch and set the value of the handled variable to true The
logic that handles the event can call the get_argument method on the second parameter
passed into the onBubbleEvent method to access the command argument In this case, the
Trang 11GridView client control’s handling of the Select and Delete events is pretty simple — the control simply calls the alert method to display a message that contains the value returned from the get_argument method
6 Return the value of the handled variable to the caller of the onBubbleEvent The caller of the onBubbleEvent method of a parent client control is the raiseBubbleEvent method of the child client control as shown in the following code snippet from Listing 8-17 The child client control calls the raiseBubbleEvent method to bubble its event up to its parent
function Sys$UI$Control$raiseBubbleEvent(source, args) {
var currentTarget = this.get_parent();
while (currentTarget) {
if (currentTarget.onBubbleEvent(source, args)) return;
currentTarget = currentTarget.get_parent();
}}
The raiseBubbleEvent method marches upward through the containment hierarchy of the control that invokes the raiseBubbleEvent and keeps calling the onBubbleEvent method on each node of the hierarchy until it reaches the node whose onBubbleEvent method returns true In this case, the
onBubbleEvent method of the GridView client control returns true after handling the Select and
$create(CustomComponents.GridView, null, null, null, $get(“products”));
$create(Sys.Preview.UI.Button, { command: “Select”, argument: “Product1” }, null,
{ parent: “products”}, $get(“product1Selectbtn1”));
(continued)
Trang 12{ command: “Select”, argument: “Product2” },
null, { parent: “products”}, $get(“product2Selectbtn1”));
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<table id=”products” style=”background-color:LightGoldenrodYellow;
border-color:Tan; border-width:1px; color:Black” cellpadding=”0”>
<td><button id=”product1Selectbtn1” type=”button”>Select</button></td>
<td><button id=”product1Deletebtn1” type=”button”>Delete</button></td>
</tr>
<tr id=”row2” style=”background-color:PaleGoldenrod”>
<td>Product2</td>
<td>$200</td>
<td><button id=”product2Selectbtn1” type=”button”>Select</button></td>
<td><button id=”product2Deletebtn1” type=”button”>Delete</button></td>
Trang 13This page renders a table with the id HTML attribute value of “products ” that contains two table rows Each table row contains two table cells, and each table cell contains a <button> HTML element There-fore, altogether you’re looking at four <button> HTML elements with id HTML attribute values of
product1Selectbtn1 , product1Deletebtn1 , product2Selectbtn1 , and product2Deletebtn1 Notice that each <button> HTML element displays either the Select or the Delete text
As you can see, we’ve basically hard-coded a table of rows where each row displays one product and two buttons One button allows you to select the row and the other button allows you to delete the row
To keep this discussion focused, the onBubbleEvent method of the GridView client control does not contain the logic that actually selects or deletes a row Instead, this method simply calls the alert method to inform the user that a specified product is selected or deleted (see Listing 9-13 )
<table id=”products” style=”background-color:LightGoldenrodYellow;
border-color:Tan; border-width:1px; color:Black” cellpadding=”0”>
<td><button id=”product1Selectbtn1” type=”button”>Select</button></td>
<td><button id=”product1Deletebtn1” type=”button”>Delete</button></td>
</tr>
<tr id=”row2” style=”background-color:PaleGoldenrod”>
<td>Product2</td>
<td>$200</td>
<td><button id=”product2Selectbtn1” type=”button”>Select</button></td>
<td><button id=”product2Deletebtn1” type=”button”>Delete</button></td>
</tr>
</table>
Figure 9-1