Tải bản đầy đủ (.pdf) (26 trang)

ASP.NET AJAX Programmer’s Reference - Chapter 9 ppsx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (278.57 KB, 26 trang )

Event 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)


c09.indd 323c09.indd 323 8/21/07 1:02:52 AM8/21/07 1:02:52 AM
Chapter 9: Event Bubbling and Button Client Control
324
Listing 9-1 (continued)
Sys.Preview.UI.CommandEventArgs.prototype =
{
get_argument: Sys$Preview$UI$CommandEventArgs$get_argument,
get_commandName: Sys$Preview$UI$CommandEventArgs$get_commandName
}

Sys.Preview.UI.CommandEventArgs.descriptor =
{
properties: [ {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
properties:
Sys.Preview.UI.CommandEventArgs =
function Sys$Preview$UI$CommandEventArgs(commandName, argument)
{
Sys.Preview.UI.CommandEventArgs.initializeBase(this);
this._commandName = commandName;
this._argument = argument;
}
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);
c09.indd 324c09.indd 324 8/21/07 1:02:54 AM8/21/07 1:02:54 AM
Chapter 9: Event Bubbling and Button Client Control
325
Button 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 Command-
Name
property to the CommandName property of the CommandEventArgs object that it passes into
the event handlers registered for its
Command event.
❑ 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 meth-
ods because they’re directly defined on the prototype property of the class. As such, you must invoke
these methods on a class instance.

c09.indd 325c09.indd 325 8/21/07 1:02:55 AM8/21/07 1:02:55 AM
Chapter 9: Event Bubbling and Button Client Control
326
Listing 9-3: The prototype Property of the Button Client Control
Sys.Preview.UI.Button.prototype =
{
_command: null,
_arg: null,
_clickHandler: null,
get_argument: Sys$Preview$UI$Button$get_argument,
set_argument: Sys$Preview$UI$Button$set_argument,
get_command: Sys$Preview$UI$Button$get_command,
set_command: Sys$Preview$UI$Button$set_command,
initialize: Sys$Preview$UI$Button$initialize,
dispose: Sys$Preview$UI$Button$dispose,
add_click: Sys$Preview$UI$Button$add_click,
remove_click: Sys$Preview$UI$Button$remove_click,
_onClick: Sys$Preview$UI$Button$_onClick
}
argument
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
function Sys$Preview$UI$Button$get_argument()
{
return this._arg;
}

function Sys$Preview$UI$Button$set_argument(value)
{
if (this._arg !== value)
{
this._arg = value;
this.raisePropertyChanged(‘argument’);
}
}
command
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
c09.indd 326c09.indd 326 8/21/07 1:02:55 AM8/21/07 1:02:55 AM

Chapter 9: Event Bubbling and Button Client Control
327
the 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’);
}
}
initialize
The Button client control overrides the initialize method that it inherits from its base class, as shown
in Listing 9-6 .
Listing 9-6: The initialize Method of the Button Client Control
function Sys$Preview$UI$Button$initialize()
{
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.
c09.indd 327c09.indd 327 8/21/07 1:02:55 AM8/21/07 1:02:55 AM
Chapter 9: Event Bubbling and Button Client Control
328
add_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

function Sys$Preview$UI$Button$add_click(handler)
{
this.get_events().addHandler(“click”, handler);
}

function Sys$Preview$UI$Button$remove_click(handler) {
this.get_events().removeHandler(“click”, handler);
}
_ on Click
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
function Sys$Preview$UI$Button$_onClick()
{
var handler = this.get_events().getHandler(“click”);
if (handler)
handler(this, Sys.EventArgs.Empty);


if (this._command)
{
var e = new Sys.Preview.UI.CommandEventArgs(this._command, this._arg);
this.raiseBubbleEvent(this, e);
}
}
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:
c09.indd 328c09.indd 328 8/21/07 1:02:56 AM8/21/07 1:02:56 AM
Chapter 9: Event Bubbling and Button Client Control
329
var 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’);
}

c09.indd 329c09.indd 329 8/21/07 1:02:56 AM8/21/07 1:02:56 AM
Chapter 9: Event Bubbling and Button Client Control
330
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
client control.
Listing 9-11: The GridView.js JavaScript File Containing the GridView Client Control
Implementation
Type.registerNamespace(“CustomComponents”);

CustomComponents.GridView = function CustomComponents$GridView(associatedElement)
{
CustomComponents.GridView.initializeBase(this, [associatedElement]);
}

function CustomComponents$GridView$onBubbleEvent(source, args)
{
c09.indd 330c09.indd 330 8/21/07 1:02:56 AM8/21/07 1:02:56 AM
Chapter 9: Event Bubbling and Button Client Control
331
var handled = false;
if (args instanceof Sys.Preview.UI.CommandEventArgs)
{

switch (args.get_commandName())
{
case “Select”:
alert(args.get_argument() + “ is selected!”);
handled = true;
break;
case “Delete”:
alert(args.get_argument() + “ is deleted!”);
handled = true;
break;
}
}
return handled;
}

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);
c09.indd 331c09.indd 331 8/21/07 1:02:57 AM8/21/07 1:02:57 AM
Chapter 9: Event Bubbling and Button Client Control
332
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)
{
switch (args.get_commandName())
{
case “Select”:
alert(args.get_argument() + “ is selected!”);
handled = true;
break;
case “Delete”:
alert(args.get_argument() + “ is deleted!”);
handled = true;
break;
}
}
return handled;
}
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
c09.indd 332c09.indd 332 8/21/07 1:02:57 AM8/21/07 1:02:57 AM
Chapter 9: Event Bubbling and Button Client Control
333
GridView 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

Delete events.
Listing 9-14 contains a Web page that demonstrates the
GridView and Button client controls’ event-
bubbling capabilities. Figure 9-1 shows what you’ll see when you access this page.
Listing 9-14: A Page Showing the GridView and Button Client Controls’ Event-Bubbling
Capabilities
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ />
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
<script type=”text/javascript” language=”javascript”>
function pageLoad()
{
$create(CustomComponents.GridView, null, null, null, $get(“products”));

$create(Sys.Preview.UI.Button,
{ command: “Select”, argument: “Product1” },
null,
{ parent: “products”},
$get(“product1Selectbtn1”));
(continued)

c09.indd 333c09.indd 333 8/21/07 1:02:57 AM8/21/07 1:02:57 AM
Chapter 9: Event Bubbling and Button Client Control
334
Listing 9-14 (continued)
$create(Sys.Preview.UI.Button,
{ command: “Delete”, argument: “Product1” },
null,
{ parent: “products”},
$get(“product1Deletebtn1”));

$create(Sys.Preview.UI.Button,
{ command: “Select”, argument: “Product2” },
null, { parent: “products”}, $get(“product2Selectbtn1”));

$create(Sys.Preview.UI.Button,
{ command: “Delete”, argument: “Product2” },
null,
{ parent: “products”},
$get(“product2Deletebtn1”));
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<Scripts>
<asp:ScriptReference Assembly=”Microsoft.Web.Preview”
Name=”PreviewScript.js” />
<asp:ScriptReference Path=”GridView.js” />
</Scripts>

</asp:ScriptManager>
<table id=”products” style=”background-color:LightGoldenrodYellow;
border-color:Tan; border-width:1px; color:Black” cellpadding=”0”>
<tr style=”background-color:Tan; font-weight:bold”>
<th>Product Name</th>
<th>Unit Price</th>
</tr>
<tr id=”row1”>
<td>Product1</td>
<td>$100</td>
<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>
</form>
</body>
</html>
c09.indd 334c09.indd 334 8/21/07 1:02:57 AM8/21/07 1:02:57 AM
Chapter 9: Event Bubbling and Button Client Control
335
This 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”>
<tr style=”background-color:Tan; font-weight:bold”>
<th>Product Name</th>
<th>Unit Price</th>
</tr>
<tr id=”row1”>
<td>Product1</td>
<td>$100</td>
<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
c09.indd 335c09.indd 335 8/21/07 1:02:58 AM8/21/07 1:02:58 AM
Chapter 9: Event Bubbling and Button Client Control
336
Now, lets walk though the implementation of the pageLoad method shown in Listing 9-14 . This method
first instantiates an instance of the
GridView client control to represent the table DOM element with id
HTML attribute value of “
products ”:
$create(Sys.Preview.UI.GridView, null, null, null, $get(“products”));
Then, the method instantiates a Button client control to represent the button DOM element with the id
HTML attribute value of
product1Selectbtn1 . This button DOM element allows the user to select the
first row:
$create(Sys.Preview.UI.Button,
{ command: “Select”, argument: “Product1” },
null,
{ parent: “products”},
$get(“product1Selectbtn1”));
The following object literal is passed as the third argument into the create static method:
{ parent: “products”}
The third argument of the create method specifies the values of the properties of the component that
reference other components in the current application. As discussed in Chapter 8 , the
Control base class
exposes a property name
parent that references the parent client control of the current control. In this
case, the
{parent: “products”} object literal is passed as the third parameter of the create method
that creates the

product1Selectbtn1 client control to tell the create method that the client control
with an
id property value of “products” is the parent control of the product1Selectbtn1 client
control. The
create method internally locates this parent control in the _components collection of
the current
Application object and assigns it to the parent property of the product1Selectbtn1
client control.
The
pageLoad method passes the {command: “Select”, argument: “Product1”} object literal as
the second parameter of the
create method that creates the product1Selectbtn1 client control. As
you can see, this object literal specifies the string
“Product1” as the value of the argument property of
this client control. As you saw previously in Listing 9-13 , this allows the
onBubbleEvent method of the

GridView client control to use the get_argument method to access the argument value and conse-
quently the name of the product being selected:
The
pageLoad method follows similar steps to instantiate and initialize the Button client controls that
represent the
<button> HTML elements with id HTML attribute values of product1Deletebtn1 ,

product2Selectbtn1 , and product2Deletebtn1 .
Finally, this page registers the
PreviewScript.js and GridView.js JavaScript files with the

ScriptManager :
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>

<Scripts>
<asp:ScriptReference Assembly=”Microsoft.Web.Preview”
Name=”PreviewScript.js” />
<asp:ScriptReference Path=”GridView.js” />
</Scripts>
</asp:ScriptManager>
c09.indd 336c09.indd 336 8/21/07 1:03:01 AM8/21/07 1:03:01 AM
Chapter 9: Event Bubbling and Button Client Control
337
Run the page and click the Delete button that deletes the second row. This should pop up the message
shown in Figure 9-2 . This message contains the name of the product being deleted.
(continued)
Figure 9-2
Bubbling an Event
The next example shows you how to use the raiseBubbleEvent method in your own custom client
control to bubble the events that your control exposes. This example involves three ASP.NET AJAX
classes: a new version of the
GridView client control, a new client control named GridViewRow , and a
new event data class named
GridViewCommandEventArgs .
GridViewRow
Listing 9-15 presents a JavaScript file named GridViewRow.js . This file contains the implementation of
a new client control named
GridViewRow that represents a table row in the GridView client control.
Listing 9-15: The GridViewRow.js JavaScript file Containing the GridViewRow Client
Control Implementation
Type.registerNamespace(“CustomComponents”);

CustomComponents.GridViewRow =
function CustomComponents$GridViewRow(associatedElement)

{
CustomComponents.GridViewRow.initializeBase(this, [associatedElement]);
}

c09.indd 337c09.indd 337 8/21/07 1:03:01 AM8/21/07 1:03:01 AM
Chapter 9: Event Bubbling and Button Client Control
338
Listing 9-15 (continued)
function CustomComponents$GridViewRow$set_rowIndex(value)
{
if (this._rowIndexSet)
throw Error.invalidOperation(“rowIndex property cannot be set twice!”);
this._rowIndexSet = true;
this._rowIndex = value;
}

function CustomComponents$GridViewRow$get_rowIndex()
{
return this._rowIndex;
}

function CustomComponents$GridViewRow$onBubbleEvent(source, args)
{
var handled = false;

if (args instanceof Sys.Preview.UI.CommandEventArgs)
{
var args2=new CustomComponents.GridViewCommandEventArgs(this, source, args);
this.raiseBubbleEvent(this, args2);
handled = true;

}

return handled;
}

CustomComponents.GridViewRow.prototype =
{
get_rowIndex : CustomComponents$GridViewRow$get_rowIndex,
set_rowIndex : CustomComponents$GridViewRow$set_rowIndex,
onBubbleEvent : CustomComponents$GridViewRow$onBubbleEvent
}

CustomComponents.GridViewRow.descriptor =
{
properties : [{name : ‘rowIndex’, type : Number}]
};

CustomComponents.GridViewRow.registerClass( “CustomComponents.GridViewRow”,
Sys.UI.Control);

if(typeof(Sys)!==’undefined’)
Sys.Application.notifyScriptLoaded();
The following sections discuss the implementation of the members of the GridViewRow client control.
c09.indd 338c09.indd 338 8/21/07 1:03:01 AM8/21/07 1:03:01 AM
Chapter 9: Event Bubbling and Button Client Control
339
Constructor
Listing 9-16 shows the constructor of the GridViewRow client control. This constructor takes an
argument that references the table row DOM element the control represents. At the end of the listing, the


GridViewRow class is registered as the subclass of the Control base class.
Listing 9-16: The Constructor of the GridViewRow Client Control
CustomComponents.GridViewRow =
function CustomComponents$GridViewRow(associatedElement)
{
CustomComponents.GridViewRow.initializeBase(this, [associatedElement]);

}
CustomComponents.GridViewRow.registerClass(“CustomComponents.GridViewRow”,
Sys.UI.Control);
row Index
The GridViewRow client control exposes a property of type integer named rowIndex that specifies the
index of the table row DOM element that the control represents. The
get_rowIndex method of the
control, shown in Listing 9-17 , returns the value of the
rowIndex property. As you’ll see later, you use
the
set_rowIndex method to set the value of this property when you create the GridViewRow client
control. This property value can be set only once, which ensures that the value cannot be overridden.
Listing 9-17: The get_row Index and set_row Index Methods of the GridViewRow Client
Control
function CustomComponents$GridViewRow$set_rowIndex(value)
{
if (this._rowIndexSet)
throw Error.invalidOperation(“rowIndex property cannot be set twice!”);

this._rowIndexSet = true;
this._rowIndex = value;
}


function CustomComponents$GridViewRow$get_rowIndex()
{
return this._rowIndex;
}
on BubbleEvent
As you can see in Listing 9-18 , the GridViewRow client control overrides the onBubbleEvent method of
its base class to catch the events that its child client controls fire. In this case, the table row DOM element
that the
GridViewRow client control represents contains two Button client controls that allow the end
user to select and delete the table row. Therefore, the
onBubbleEvent method of the GridViewRow
control catches the
command events that these Button client controls raise. As such, the first parameter of
the
onBubbleEvent method references the Button client control that raised the command event, and the
second parameter of the method references the
CommandEventArgs event data object that the _onClick
c09.indd 339c09.indd 339 8/21/07 1:03:02 AM8/21/07 1:03:02 AM
Chapter 9: Event Bubbling and Button Client Control
340
method of the Button client control passed into the raiseBubbleEvent method (shown previously in
Listing 9-8 ).
Listing 9-18: The on BubbleEvent Method of the GridViewRow Client Control
function CustomComponents$GridViewRow$onBubbleEvent(source, args)
{
var handled = false;

if (args instanceof Sys.Preview.UI.CommandEventArgs)
{
var args2=new CustomComponents.GridViewCommandEventArgs(this,source, args);

this.raiseBubbleEvent(this, args2);
handled = true;
}

return handled;
}
This method first checks whether the event that it just caught is a command event. The easiest way to do
this is to check whether the event data object passed into the
onBubbleEvent method as its second
argument is of type
CommandEventArgs :
if (args instanceof Sys.Preview.UI.CommandEventArgs)
As previously shown in Listing 9-8 , only command events use this type as their event data class.
Next, the
onBubbleEvent method creates an instance of the GridViewCommandEventArgs class.
As you’ll see in the next section, this class is the event data class associated with an event named

GridViewCommand . The constructor of this event data class takes three arguments. The first argument
references the
GridViewRow client control; the second argument references the Button client control that
raised the
Command event; and the third argument references the CommandEventArgs object passed into
the
onBubbleEvent :
var args2=new CustomComponents.GridViewCommandEventArgs(this, source, args);
Next, the onBubbleEvent method calls the raiseBubbleEvent method, passing in two parameters.
The first parameter references the
GridViewRow client control; and the second parameter references the

GridViewCommandEventArgs object:

this.raiseBubbleEvent(this, args2);
Therefore, the onBubbleEvent method catches the command event that its Button child controls raise
and raises a
GridViewCommand event instead. In this case, it returns true to stop its Button child con-
trol’s
command event from bubbling to the parent of the GridViewRow control, which is the GridView
client control. In other words, the
GridView client control never gets to handle the original command
event raised by the
Button child control. Instead, it receives and handles the GridViewCommand event
that the
GridViewRow client control raises.
c09.indd 340c09.indd 340 8/21/07 1:03:02 AM8/21/07 1:03:02 AM
Chapter 9: Event Bubbling and Button Client Control
341
descriptor
The GridViewRow client control, like any other ASP.NET AJAX class, implements a property named

descriptor . You must always define the descriptor property of your ASP.NET AJAX class on the
class itself, not on the
prototype property of your class. In other words, the descriptor property must
be a static property. This is because the
descriptor property describes the members of the class using
metadata that is shared by all instances of the class.
Listing 9-19 assigns an object literal to the
descriptor property. In this case, the object contains a single
name/value pair that describes the properties of the
GridViewRow client control. Because this control
contains a single property named
rowIndex , the value portion of this name/value pair is an array

that contains a single object literal. This object consists of three name/value pairs. The first name/value
pair specifies the name of the property:
rowIndex . The second name/value pair specifies the type of the
property:
Number .
Listing 9-19: The descriptor Property of the GridViewRow Client Control
CustomComponents.GridViewRow.descriptor =
{
properties : [{name : ‘rowIndex’, type : Number}]
};
GridViewCommandEventArgs
As discussed in the previous section, the GridViewRow client control raises an event named

GridViewCommand that uses a new event data class named GridViewCommandEventArgs . Listing 9-20
presents a JavaScript file named
GridViewCommandEventArgs.js that contains the implementation of
the
GridViewCommandEventArgs class.
Listing 9-20: The GridViewCommandEventArgs.js File Containing
GridViewCommandEventArgs Event Data Class Implementation
Type.registerNamespace(“CustomComponents”);

CustomComponents.GridViewCommandEventArgs =
function CustomComponents$GridViewCommandEventArgs (row, source, args)
{
CustomComponents.GridViewCommandEventArgs.initializeBase(this,
[args.get_commandName(), args.get_argument()]);
this._commandSource = source;
this._row = row;
}


function CustomComponents$GridViewCommandEventArgs$get_commandSource()
{
return this._commandSource;
}

(continued)
c09.indd 341c09.indd 341 8/21/07 1:03:02 AM8/21/07 1:03:02 AM
Chapter 9: Event Bubbling and Button Client Control
342
Listing 9-20 (continued)
function CustomComponents$GridViewCommandEventArgs$get_row()
{
return this._row;
}

CustomComponents.GridViewCommandEventArgs.prototype =
{
get_commandSource : CustomComponents$GridViewCommandEventArgs$get_commandSource,
get_row : CustomComponents$GridViewCommandEventArgs$get_row
};

CustomComponents.GridViewCommandEventArgs.descriptor =
{
properties: [{name:’commandSource’, type:Sys.Preview.UI.Control, readOnly:true},
{name : ‘row’, type : CustomComponents.GridViewRow, readOnly: true}]
}

CustomComponents.GridViewCommandEventArgs.registerClass(
“CustomComponents.GridViewCommandEventArgs”,

Sys.Preview.UI.CommandEventArgs);

if(typeof(Sys)!==’undefined’)
Sys.Application.notifyScriptLoaded();
The following sections walk you through the implementation of the GridViewCommandEventArgs event
data class.
Constructor
Listing 9-21 shows the constructor of the GridViewCommandEventArgs event data class. This construc-
tor takes three parameters. The first parameter references the
GridViewRow client control that bubbles
the event up to the
GridView control (shown previously in Listing 9-18 ). The second parameter refer-
ences the
Button child client control that raised the original command event (shown previously in List-
ing 9-8 ). The third parameter references the
CommandEventArgs event data object that the Button child
client’s
_onClick method instantiated (shown previously in Listing 9-8 ).
Listing 9-21: The Constructor of the GridViewCommandEventArgs Event Data Class
CustomComponents.GridViewCommandEventArgs =
function CustomComponents$GridViewCommandEventArgs (row, source, args)
{
CustomComponents.GridViewCommandEventArgs.initializeBase(this,
[args.get_commandName(), args.get_argument()]);
this._commandSource = source;
this._row = row;
}

CustomComponents.GridViewCommandEventArgs.registerClass(
“CustomComponents.GridViewCommandEventArgs”,

Sys.Preview.UI.CommandEventArgs);
c09.indd 342c09.indd 342 8/21/07 1:03:03 AM8/21/07 1:03:03 AM
Chapter 9: Event Bubbling and Button Client Control
343
The constructor first calls the initializeBase method to invoke the constructor of its CommandEvent-
Args
base class. An array of two items is passed to the constructor of the CommandEventArgs class.
These items contain the command name and argument, respectively.
The
GridViewCommandEventArgs constructor stores the references to the GridViewRow and Button
client controls in internal fields named
_commandSource and _row . Listing 9-21 registers the

GridViewCommandEventArgs class as the subclass of the CommandEventArgs class:
CustomComponents.GridViewCommandEventArgs.registerClass(
“CustomComponents.GridViewCommandEventArgs”,
Sys.Preview.UI.CommandEventArgs);
get_command Source
As you can see in Listing 9-22 , the get_commandSource method returns a reference to the Button child
control that raised the original command event.
Listing 9-22: The get_command Source Method of the GridViewCommandEventArgs
Class
function CustomComponents$GridViewCommandEventArgs$get_commandSource()
{
return this._commandSource;
}
get_row
As you can see in Listing 9-23 , the get_row method returns a reference to the GridViewRow client
control that raised the
GridViewCommand event.

Listing 9-23: The get_row Method of the GridViewCommandEventArgs Class
function CustomComponents$GridViewCommandEventArgs$get_row()
{
return this._row;
}
descriptor
The GridViewCommandEventArgs class defines a property named descriptor that returns an object
literal describing the members of the class, as shown in Listing 9-24 . In this case, the object contains a
single name/value pair that describes the properties of the class. The value part of the name/value pair
contains an array of two object literals that describe the
commandSource and row properties of the class.
Each object literal contains three name/value pairs. The first two pairs describe the name and type of the
property. The last pair specifies that the property is read-only.
c09.indd 343c09.indd 343 8/21/07 1:03:03 AM8/21/07 1:03:03 AM
Chapter 9: Event Bubbling and Button Client Control
344
Listing 9-24: The descriptor Property of the GridViewCommandEventArgs Class
CustomComponents.GridViewCommandEventArgs.descriptor =
{
properties: [{name:’commandSource’, type:Sys.Preview.UI.Control, readOnly:true},
{name : ‘row’, type : CustomComponents.GridViewRow, readOnly: true}]
}
GridView
Listing 9-25 presents a new version of the GridView.js JavaScript file that contains the implementation
of a new version of the
GridView client control.
Listing 9-25: The Content of a New Version of the GridView.js File that Contains the
Implementation of a New Version of the GridView Client Control
Type.registerNamespace(“CustomComponents”);


CustomComponents.GridView = function Sys$Preview$UI$GridView(associatedElement)
{
CustomComponents.GridView.initializeBase(this, [associatedElement]);
}

function CustomComponents$GridView$onBubbleEvent(source, args)
{
var handled = false;
if (args instanceof CustomComponents.GridViewCommandEventArgs)
{
switch (args.get_commandName())
{
case “Select”:
alert(args.get_argument() + “ from row number “ +
args.get_row().get_rowIndex() + “ is selected!”);
handled = true;
break;
case “Delete”:
alert(args.get_argument() + “ from row number “ +
args.get_row().get_rowIndex() + “ is deleted!”);
handled = true;
break;
}
}
return handled;
}

CustomComponents.GridView.prototype =
{
onBubbleEvent : CustomComponents$GridView$onBubbleEvent

}

c09.indd 344c09.indd 344 8/21/07 1:03:03 AM8/21/07 1:03:03 AM
Chapter 9: Event Bubbling and Button Client Control
345
CustomComponents.GridView.registerClass(“CustomComponents.GridView”,
Sys.UI.Control);
if(typeof(Sys)!==’undefined’)
Sys.Application.notifyScriptLoaded();
This GridView client control overrides the onBubbleEvent method of its base class to catch the events
that its
GridViewRow child client controls raise. As previously shown in Listing 9-18 , the GridViewRow
client control raises and bubbles up the
GridViewCommand event. Because the GridView client control
handles only
GridViewCommand events, onBubbleEvent first checks whether the event just caught is
of type
GridViewCommand . The standard way to do this is to check the type of the second parameter
passed into the
onBubbleEvent . This parameter references an event data object. As you can see from the
following code snippet, if this parameter is of type
GridViewCommandEventArgs , you can rest assured
that the event just caught is of type
GridViewCommand because only this type of event uses the

GridViewCommandEventArgs class as its event data class.
if (args instanceof CustomComponents.GridViewCommandEventArgs)
Next, the onBubbleEvent method does what the earlier version of the GridView client control did. The
main difference here is that the messages that the
alert methods pop up now contain the index of

the table row that raised the
GridViewCommand event (see Figure 9-3 ).
Figure 9-3
c09.indd 345c09.indd 345 8/21/07 1:03:04 AM8/21/07 1:03:04 AM
Chapter 9: Event Bubbling and Button Client Control
346
Listing 9-26 shows a page that uses the new version of the GridView client control.
Listing 9-26: A Page that Uses the GridView Control
<%@ Page Language=”C#” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
<script type=”text/javascript” language=”javascript”>
function pageLoad()
{
$create(CustomComponents.GridView, null, null, null, $get(“products”));

$create(CustomComponents.GridViewRow,
{rowIndex: 1}, null, {parent: “products”}, $get(“row1”));

$create(Sys.Preview.UI.Button,
{ command: “Select”, argument: “Product1” }, null,
{ parent: “row1”},
$get(“product1Selectbtn1”));

$create(Sys.Preview.UI.Button,
{ command: “Delete”, argument: “Product1” },
null,
{ parent: “row1”},

$get(“product1Deletebtn1”));

$create(CustomComponents.GridViewRow,
{rowIndex: 2}, null, {parent: “products”}, $get(“row2”));

$create(Sys.Preview.UI.Button,
{ command: “Select”, argument: “Product2” }, null,
{ parent: “row2”},
$get(“product2Selectbtn1”));

$create(Sys.Preview.UI.Button,
{ command: “Delete”, argument: “Product2” }, null,
{ parent: “row2”},
$get(“product2Deletebtn1”));
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<Scripts>
<asp:ScriptReference Assembly=”Microsoft.Web.Preview”
Name=”PreviewScript.js” />
<asp:ScriptReference Path=”GridViewCommandEventArgs.js” />
<asp:ScriptReference Path=”GridViewRow.js” />
<asp:ScriptReference Path=”GridView.js” />
</Scripts>
</asp:ScriptManager>

c09.indd 346c09.indd 346 8/21/07 1:03:04 AM8/21/07 1:03:04 AM

Chapter 9: Event Bubbling and Button Client Control
347
<table id=”products” style=”background-color:LightGoldenrodYellow;
border-color:Tan; border-width:1px; color:Black” cellpadding=”0”>
<tr style=”background-color:Tan; font-weight:bold”>
<th>Product Name</th>
<th>Unit Price</th>
</tr>
<tr id=”row1”>
<td>Product1</td>
<td>$100</td>
<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>
</form>
</body>
</html>
Let’s walk through the implementation of the pageLoad method shown in the listing. This method first
instantiates an instance of the
GridView client control to represent the table with an id HTML attribute
value of
products :
$create(CustomComponents.GridView, null, null, null, $get(“products”));

Then, the method instantiates an instance of the GridViewRow client control to represent the first row of
the table:
$create(CustomComponents.GridViewRow,
{rowIndex: 1},
null,
{parent: “products”},
$get(“row1”));
As this code fragment shows, the pageLoad method passes the {parent: “products”} object literal
as the third argument into the
create method to specify the GridView client control with id property
value of
“products” as the parent of the GridViewRow client control being instantiated. As you saw
previously in Listing 9-18 , the
GridViewRow client control’s onBubbleEvent method calls the

raiseBubbleEvent method to bubble the GridViewCommand event to its parent client control. There-
fore, you must specify the
GridView client control as the parent control of the GridViewRow control if
you want the
onBubbleEvent method of the GridView client control to catch the GridViewCommand
event. Also note that the
pageLoad method passes the {rowIndex: 1} object literal as the second argu-
ment into the
create method to initialize the rowIndex property of the GridViewRow client control
being instantiated to
1 .
c09.indd 347c09.indd 347 8/21/07 1:03:05 AM8/21/07 1:03:05 AM

×