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

ASP.NET AJAX Programmer’s Reference - Chapter 5 pptx

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 (142.72 KB, 30 trang )

Event Programming
Extensions
One of the great advantages of the .NET Framework is its event-programming facilities. The
ASP.NET AJAX client-side framework provides you with similar facilities to make client-side
JavaScript event programming more like server-side .NET event programming as much as possible.
This chapter provides you with in-depth coverage of the ASP.NET AJAX event-programming
extensions and examples that use these extensions.
Event Programming
The .NET Framework provides you with the following three classes to facilitate event programming
in the .NET Framework:
❑ System.EventArgs : This is the base class from which all event data classes derive, directly
or indirectly. This class exposes a single read-only property of type
EventArgs named

Empty , which simply instantiates and returns an instance of the class.
❑ System.ComponentModel.CancelEventArgs : This is the base class from which all event
data classes associated with cancelable events derive, directly or indirectly. This class exposes
a single read/write
Boolean property named Cancel .
❑ System.ComponentModel.EventHandlerList : This class is a linked list, where each list
entry contains the event handlers for an event type with a specified key. This class exposes
the following three important methods:
❑ AddHandler : This method adds a specified event handler to the list entry associated
with an event type with a specified key.
❑ RemoveHandler : This method removes a specified event handler from the list entry
associated with an event type with a specified key.
❑ AddHandlers : This method adds the content of a specified EventHandlerList — that
is, a link list of list entries — to the
EventHandlerList on which the method is called.
c05.indd 131c05.indd 131 8/20/07 6:01:48 PM8/20/07 6:01:48 PM
Chapter 5: Event Programming Extensions


132
The ASP.NET AJAX client-side framework comes with three classes named Sys.EventArgs ,
Sys. CancelEventArgs , and Sys.EventHandlerList that respectively emulate the .NET

System. EventArgs , System.ComponentModel.CancelEventArgs , and System.ComponentModel
.EventHandlerList
classes as discussed in the following sections.
Before diving into the implementation of these classes, here’s a basic description of what an event data
class is and what role it plays in server-side .NET or client-side ASP.NET AJAX event programming. An
instance of a class raises an event to inform interested clients that something of interest to the clients has
occurred. The clients of certain types of events may need more information to process the event. This
information is known as event data. The event data class is a class whose instances contain the event
data associated with a particular type of event. An event data class normally exposes properties that
contain the event data. As you’ll see later, it is the responsibility of the instance that raises the event to
instantiate an instance of the appropriate event data class, to initialize the properties of this event data
class instance with the appropriate event data, and to pass this event data class instance into the event
handlers registered for the specified event when it invokes these event handlers.
Sys.EventArgs
The ASP.NET AJAX client-side framework contains a base event data class that emulates the .NET
System.EventArgs base event data class, as shown in Listing 5-1 .
Listing 5-1: The Sys.EventArgs Base Event Data Class
Sys.EventArgs = function Sys$EventArgs() { }
Sys.EventArgs.registerClass(‘Sys.EventArgs’);
The Sys.EventArgs base event data class of the ASP.NET AJAX client-side framework, just like the

System.EventArgs base event data class of the .NET Framework, features a static property named

Empty . Here’s how it works:
Sys.EventArgs.Empty = new Sys.EventArgs();
Sys.CancelEventArgs

The ASP.NET AJAX client-side framework also includes an event data class named Sys.CancelEventArgs
that emulates the .NET
System.ComponentModel.CancelEventArgs event data class, as defined
in Listing 5-2 . The
Sys.CancelEventArgs class inherits from the Sys.EventArgs base class and
extends its functionality to add support for a new read/write Boolean property named
cancel . The
Sys.CancelEventArgs class, just like its .NET counterpart, is the base class for the event data classes
of all cancelable events in the ASP.NET AJAX client-side framework.
Listing 5-2: The Sys.CancelEventArgs Event Data Class
Sys.CancelEventArgs = function Sys$CancelEventArgs() {
Sys.CancelEventArgs.initializeBase(this);
this._cancel = false;
}

(continued)
c05.indd 132c05.indd 132 8/20/07 6:01:48 PM8/20/07 6:01:48 PM
Chapter 5: Event Programming Extensions
133
function Sys$CancelEventArgs$get_cancel() {
return this._cancel;
}

function Sys$CancelEventArgs$set_cancel(value) {
this._cancel = value;
}

Sys.CancelEventArgs.prototype = {
get_cancel: Sys$CancelEventArgs$get_cancel,
set_cancel: Sys$CancelEventArgs$set_cancel

}

Sys.CancelEventArgs.registerClass(‘Sys.CancelEventArgs’, Sys.EventArgs);
EventHandlerList
Listing 5-3 presents the definition of the Sys.EventHandlerList class.
Listing 5-3: The Sys.EventHandlerList Class
Sys.EventHandlerList = function Sys$EventHandlerList() {
this._list = {};
}

Sys.EventHandlerList.prototype = {
addHandler: Sys$EventHandlerList$addHandler,
removeHandler: Sys$EventHandlerList$removeHandler,
getHandler: Sys$EventHandlerList$getHandler,

_getEvent: Sys$EventHandlerList$_getEvent
}

Sys.EventHandlerList.registerClass(‘Sys.EventHandlerList’);
As you can see, the constructor of this class simply instantiates an internal object named _list :
this._list = {};
Also note that this class features four methods: addHandler , removeHandler , getHandler , and

_getEvent . The definitions of these methods are presented in the following sections.
_ get Event
The Sys.EventHandlerList class contains an internal method named _getEvent as defined in
Listing 5-4 . As mentioned, this method is used internally by other methods of the class, which means
that you should not directly use this method in your JavaScript code. Instead, you should use the other
methods of the class. However, understanding the internal implementation of this method helps you get
a better understanding of the other methods of the class.

c05.indd 133c05.indd 133 8/20/07 6:01:49 PM8/20/07 6:01:49 PM
Chapter 5: Event Programming Extensions
134
Listing 5-4: The _ get Event Method
function Sys$EventHandlerList$_getEvent(id, create) {
if (!this._list[id]) {
if (!create)
return null;
this._list[id] = [];
}
return this._list[id];
}
As you can see, the _getEvent method takes two arguments. The first argument is used as an index into
the
_list . The second argument is a Boolean value that specifies whether the method should instantiate
a subarray associated with the specified index if the
_list does not already contain the subarray. In
summary, the
_getEvent method uses its first argument as an index into the _list to return the
subarray associated with the index.
add Handler
This method adds a specified event handler to the subarray of the _list with the specified index. This sub-
array contains the event handlers for the event type associated with the specified index. As such, this method
takes two arguments. The first argument is used as an index into the
_list to access the associated subarray.
The second argument references the event handler being added. As Listing 5-5 shows,
addHandler first calls
the
_getEvent method to return the subarray associated with the specified index, and then calls the add
method on the

Array class to add the specified event handler to this subarray.
Listing 5-5: The add Handler Method
function Sys$EventHandlerList$addHandler(id, handler) {
Array.add(this._getEvent(id, true), handler);
}
remove Handler
This method removes a specified event handler from the subarray of the _list with the specified
index. This subarray contains the event handlers for the event type associated with the specified index.
As such, this method takes two arguments, as shown in Listing 5-6 . The first argument is used as an
index into the
_list to access the associated subarray. The second argument references the event
handler being removed.
Listing 5-6: The remove Handler Method
function Sys$EventHandlerList$removeHandler(id, handler) {
var evt = this._getEvent(id);
if (!evt)
return;
Array.remove(evt, handler);
}
c05.indd 134c05.indd 134 8/20/07 6:01:49 PM8/20/07 6:01:49 PM
Chapter 5: Event Programming Extensions
135
As you can see, removeHandler first calls the _getEvent method to access the subarray associated with
the specified index and then calls the
remove method on the Array class to remove the specified event
handler from the subarray.
get Handler
This method returns a reference to a JavaScript function whose invocation automatically invokes all
the event handlers for an event type with a specified index. See Listing 5-7 for the implementation of
this method.

Listing 5-7: The get Handler Class
function Sys$EventHandlerList$getHandler(id) {
var evt = this._getEvent(id);
if (!evt || (evt.length === 0))
return null;
evt = Array.clone(evt);
if (!evt._handler) {
evt._handler = function(source, args) {
for (var i = 0, l = evt.length; i < l; i++) {
evt[i](source, args);
}
};
}
return evt._handler;
}
As you can see, getHandler first calls the _getEvent method to access the subarray of the _list with
the specified index:
var evt = this._getEvent(id);
Then it defines a function that iterates through the event handlers in this subarray and invokes each
enumerated event handler:
evt._handler = function(source, args) {
for (var i = 0, l = evt.length; i < l; i++) {
evt[i](source, args);
}
};
One of the great features of the ASP.NET Framework is its convenient event programming pattern for
implementing a new event. That is, adding a new event to a class involves the following steps:
1. Add a property of type EventHandlerList to your class if your class does not already contain
this property.
2. Choose an appropriate name for your event.

c05.indd 135c05.indd 135 8/20/07 6:01:49 PM8/20/07 6:01:49 PM
Chapter 5: Event Programming Extensions
136
3. Choose an appropriate key for your event. The key is normally an instance of the
System.Object class.
4. Determine whether your class must pass data to the event subscribers when it raises the event.
If so, proceed to step 5. If not, use the
EventArgs and EventHandler base classes as your event
data class and event delegate, and proceed to step 9 (skipping steps 5 through 8).
5. Determine whether the .NET Framework or your own custom library already comes with an
event data class and event delegate that you can use directly. If so, skip steps 6, 7, and 8 and go
directly to step 9. Otherwise, proceed to the next step.
6. Determine which event data class of the .NET Framework or your own custom library is the
most appropriate base class.
7. Implement an event data class that derives from the base class chosen in step 6.
8. Define an event delegate that takes two arguments where the first argument is of type
System.Object and the second argument is of the same type as your event data class.
9. Declare an event with the same type as your event delegate as the member of your class. The

add and remove event accessors must add and remove the specified event handler for the event
type with the specified key to the
EventHandlerList property of your class.
10. Add a method to your class that raises the event. This method must first access the list entry in
the
EventHandlerList link list that contains the event handlers for the event type with the
specified key. This list entry exposes a delegate property whose invocation automatically
invokes the event handlers that the list entry contains in the order in which they were added to
the list entry.
Following the ASP.NET Framework, the ASP.NET AJAX client-side framework offers this similar event
programming pattern:

1. Add a method named get_events to your class if your class does not already contain this
method. The method must return an instance of the
EventHandlerList type. This instance is
where your class must store all the event handlers registered for its events. A typical implemen-
tation of this method is as follows:
function get_events()
{
if (!this.events)
this.events = new Sys.EventHandlerList();

return this.events;
}
2. Choose an appropriate name for your event.
3. Determine whether your class must pass data to the event subscribers when it raises the event.
If so, proceed to step 4. If not, use the
EventArgs base class as your event data class, skip steps
4 through 6, and go directly to Step 7.
4. Determine whether the ASP.NET AJAX client-side framework or your own custom library
already comes with an event data class that you can directly use. If so, skip steps 5 and 6 and go
directly to step 7. Otherwise, proceed to step 5.
5. Determine which event data class of the ASP.NET AJAX client-side framework or your own cus-
tom library is the most appropriate base class.
c05.indd 136c05.indd 136 8/20/07 6:01:50 PM8/20/07 6:01:50 PM
Chapter 5: Event Programming Extensions
137
6. Implement an event data class that derives from the base class chosen in step 5.
7. Implement a method named add_EventName where the EventName is the placeholder for the
name of the event. The clients of your class will use this method to register event handlers for
the event with the specified name. A typical implementation of this method is as follows:
function add_EventName(handler)

{
var eventHandlerList = this.get_events();
eventHandlerList.addHandler(“EventName”, handler);
}
This method must take a single argument that references a JavaScript function and perform the
following tasks:
1. It must invoke the get_events method to return a reference to the EventHandlerList
object where the class stores all the event handlers registered for its events.
2. It must invoke the addHandler method on this EventHandlerList object to add the
specified event handler to the list of event handlers registered for the event with the speci-
fied name.
8. Implement a method named remove_EventName where the EventName is the placeholder for
the name of the event. The clients of your class will use this method to remove event handlers
from the list of event handlers registered for the event with the specified name. A typical imple-
mentation of this method is as follows:
function remove_EventName(handler)
{
var eventHandlerList = this.get_events();
eventHandlerList.removeHandler(“EventName”, handler);
}
This method must take a single argument that references a JavaScript function and perform the
following tasks:
1. It must invoke the get_events method to return a reference to the EventHandlerList
object where the class stores all the event handlers registered for its events.
2. It must invoke the removeHandler method on this EventHandlerList object to remove
the specified event handler from the list of event handlers registered for the event with a
specified name.
9. Implement a method named onEventName where the EventName is the placeholder for the
name of the event. Your class must use this method to raise the event and consequently to
invoke the event handlers registered for the event with the specified name. A typical implemen-

tation of this method is as follows:
function onEventName(e)
{
var eventHandlerList = this.get_events();
var handler = eventHandlerList.getHandler(“EventName”);
if (handler)
handler(this, e);
}
c05.indd 137c05.indd 137 8/20/07 6:01:50 PM8/20/07 6:01:50 PM
Chapter 5: Event Programming Extensions
138
This method must take a single argument that references the event data class instance that
contains the event data and perform the following tasks:
1. It must invoke the get_events method to return a reference to the EventHandlerList
object where the class stores all the event handlers registered for its events.
2. It must invoke the getHandler method on this EventHandlerList object, passing in
the name of the event. This method returns a reference to a JavaScript function. This
function automatically invokes all the event handlers registered for the event with the
specified name.
10. Implement a method that includes the logic that instantiates the event data class instance, initial-
izes the properties of this instance with the event data, and invokes the
onEventName method,
passing in the event data class instance. You’ll see an example of this later in the chapter.
Using Event Programming
This section shows you how to use the previously mentioned event programming pattern to add new
events to your client-side classes. The example used in this section is a shopping cart application. First,
the basic classes of the application are presented, and then the application is enhanced with events.
Base Classes
Listing 5-8 presents the content of a JavaScript file named ShoppingCart.js that contains the
implementation of the base classes. As you can see, the example shopping cart application consists of

two base classes:
❑ ShoppingCartItem : As the name suggests, the instances of this class represent the shopping
cart items that the end user adds to the shopping cart.
❑ ShoppingCart : As the name implies, the instances of this class represent the user’s shopping carts.
Listing 5-8: The Content of the ShoppingCart.js JavaScript File
Type.registerNamespace(“Shopping”);

Shopping.ShoppingCartItem = function Shopping$ShoppingCartItem(id, name, price)
{
this.id = id;
this.name = name;
this.price = price;
}

function Shopping$ShoppingCartItem$get_id()
{
return this.id;
}

function Shopping$ShoppingCartItem$get_name()
{
return this.name;
}

c05.indd 138c05.indd 138 8/20/07 6:01:50 PM8/20/07 6:01:50 PM
Chapter 5: Event Programming Extensions
139
function Shopping$ShoppingCartItem$get_price()
{
return this.price;

}

Shopping.ShoppingCartItem.prototype =
{
get_id : Shopping$ShoppingCartItem$get_id,
get_name : Shopping$ShoppingCartItem$get_name,
get_price : Shopping$ShoppingCartItem$get_price
};

Shopping.ShoppingCartItem.registerClass(“Shopping.ShoppingCartItem”);

Shopping.ShoppingCart = function() {
}

function Shopping$ShoppingCart$initialize()
{
this.shoppingCartItems = {};
}

function Shopping$ShoppingCart$get_shoppingCartItems()
{
return this.shoppingCartItems;
}

function Shopping$ShoppingCart$addShoppingCartItem(shoppingCartItem)
{
var cartItems = this.get_shoppingCartItems();
var cartItemId = shoppingCartItem.get_id();

if (cartItems[cartItemId])

{
var exception = Error.duplicateItem(“Duplicate Shopping Cart Item!”,
{name: shoppingCartItem.get_name()});
throw exception;
}

else
this.shoppingCartItems[cartItemId] = shoppingCartItem;
}

Shopping.ShoppingCart.prototype = {
addShoppingCartItem : Shopping$ShoppingCart$addShoppingCartItem,
initialize : Shopping$ShoppingCart$initialize,
get_shoppingCartItems : Shopping$ShoppingCart$get_shoppingCartItems
};

Shopping.ShoppingCart.registerClass(“Shopping.ShoppingCart”);

if(typeof(Sys)!==’undefined’)
Sys.Application.notifyScriptLoaded();
c05.indd 139c05.indd 139 8/20/07 6:01:51 PM8/20/07 6:01:51 PM
Chapter 5: Event Programming Extensions
140
Listing 5-9 presents an ASP.NET page that uses these base classes, which are discussed in more
detail later.
Listing 5-9: A Page that uses the Base Classes
<%@ 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”
src=”ShoppingCartApp1.js”>
</script>
<script type=”text/javascript” language=”javascript”>
function pageLoad()
{
var shoppingCart = new Shopping.ShoppingCart();
shoppingCart.initialize();
var shoppingCartItem = new Shopping.ShoppingCartItem(1, “item1”, 23);
shoppingCart.addShoppingCartItem(shoppingCartItem);
var shoppingCartItems = shoppingCart.get_shoppingCartItems();
for (var id in shoppingCartItems)
{
alert(shoppingCartItems[id].get_name());
}
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScripManager1”>
<Scripts>
<asp:ScriptReference Path=”ShoppingCart.js” />
</Scripts>
</asp:ScriptManager>
</form>
</body>
</html>

As you can see from Listing 5-9 , the pageLoad method first instantiates a ShoppingCart object to
represent the current user’s shopping cart:
var shoppingCart = new Shopping.ShoppingCart();
Next, it calls the initialize method (discussed in more detail later) on the newly instantiated

ShoppingCart object to initialize the object:
shoppingCart.initialize();
c05.indd 140c05.indd 140 8/20/07 6:01:51 PM8/20/07 6:01:51 PM
Chapter 5: Event Programming Extensions
141
Then, it instantiates a ShoppingCartItem object to represent the item that the current user wants to add
to her shopping cart:
var shoppingCartItem = new Shopping.ShoppingCartItem(1, “item1”, 23);
To keep this discussion focused, I’ve skipped the user interface that presents the current user with the list
of available items to choose from and hard-coded the item being added.
Next, the
pageLoad method adds the newly instantiated ShoppingCartItem object to the current user’s
shopping cart:
shoppingCart.addShoppingCartItem(shoppingCartItem);
Finally, it pops up a message that displays the name of the item just added to the shopping cart:
alert(shoppingCart.get_shoppingCartItems()[0].get_name());
Namespace
The ShoppingCart.js JavaScript file defines a namespace named Shopping that will contain all the
other classes of the shopping cart application, as follows:
Type.registerNamespace(“Shopping”);
ShoppingCartItem
The ShoppingCart.js JavaScript file defines a class named ShoppingCartItem , as shown in
Listing 5-10 .
Listing 5-10: The ShoppingCartItem Class
Shopping.ShoppingCartItem = function Shopping$ShoppingCartItem(id, name, price)

{
this.id = id;
this.name = name;
this.price = price;
}

Shopping.ShoppingCartItem.prototype =
{
get_id : Shopping$ShoppingCartItem$get_id,
get_name : Shopping$ShoppingCartItem$get_name,
get_price : Shopping$ShoppingCartItem$get_price
};

Shopping.ShoppingCartItem.registerClass(“Shopping.ShoppingCartItem”);
As you can see, the ShoppingCartItem class exposes three properties named id , name , and price .
The
id property of a ShoppingCartItem object uniquely identifies the object among other
c05.indd 141c05.indd 141 8/20/07 6:01:51 PM8/20/07 6:01:51 PM
Chapter 5: Event Programming Extensions
142
ShoppingCartItem objects. Notice that Listing 5-10 assigns the following object to the prototype
property of the
ShoppingCartItem class:
{
get_id : Shopping$ShoppingCartItem$get_id,
get_name : Shopping$ShoppingCartItem$get_name,
get_price : Shopping$ShoppingCartItem$get_price
};
The object shown in this code fragment exposes three methods named get_id , get_name , and get_price ,
which respectively reference three JavaScript functions named

Shopping$ShoppingCartItem$get_id ,
Shopping$ShoppingCartItem$get_name , and Shopping$ShoppingCartItem$get_price .
This ensures that all instances of the
ShoppingCartItem class share the same copy of the get_id ,
get_name , and get_price methods. If you were to directly define these three methods inside the con-
structor of the
ShoppingCartItem class, each instance of the class would have its own copy of these
methods. This would waste a lot of resources.
As Listing 5-11 shows, the
Shopping$ShoppingCartItem$get_id , Shopping$ShoppingCartItem$get
_name
, and Shopping$ShoppingCartItem$get_price methods respectively return the id, name, and
price of the associated
ShoppingCartItem object.
Listing 5-11: The Referenced JavaScript Functions
function Shopping$ShoppingCartItem$get_id()
{
return this.id;
}

function Shopping$ShoppingCartItem$get_name()
{
return this.name;
}

function Shopping$ShoppingCartItem$get_price()
{
return this.price;
}
ShoppingCart

Listing 5-12 shows the implementation of the ShoppingCart class.
Listing 5-12: The ShoppingCart Class
Shopping.ShoppingCart = function() {
}

Shopping.ShoppingCart.prototype = {
addShoppingCartItem : Shopping$ShoppingCart$addShoppingCartItem,
(continued)
c05.indd 142c05.indd 142 8/20/07 6:01:51 PM8/20/07 6:01:51 PM
Chapter 5: Event Programming Extensions
143
initialize : Shopping$ShoppingCart$initialize,
get_shoppingCartItems : Shopping$ShoppingCart$get_shoppingCartItems
};

Shopping.ShoppingCart.registerClass(“Shopping.ShoppingCart”);
In this listing, the following object is added to the prototype property of the ShoppingCart class:
{
addShoppingCartItem : Shopping$ShoppingCart$addShoppingCartItem,
initialize : Shopping$ShoppingCart$initialize,
get_shoppingCartItems : Shopping$ShoppingCart$get_shoppingCartItems
};
This object features three methods named addShoppingCartItem , initialize , and get_shopping-
CartItems
, which respectively reference the Shopping$ShoppingCart$addShoppingCartItem ,
Shopping$ShoppingCart$initialize , and Shopping$ShoppingCart$get_shoppingCartItems
JavaScript functions, as discussed in the following sections.
initialize
As you can see in Listing 5-13 , the initialize JavaScript function instantiates an internal object
named

shoppingCartItems that will contain the ShoppingCartItems added to the current user’s
shopping cart.
Listing 5-13: The initialize JavaScript Function
function Shopping$ShoppingCart$initialize()
{
this.shoppingCartItems = {};
}
get _ shopping CartItems
As Listing 5-14 shows, this JavaScript function returns a reference to the shoppingCartItems internal
array that contains the
ShoppingCartItem objects added to the current user’s shopping cart.
Listing 5-14: The get _ shopping CartItems JavaScript Function
function Shopping$ShoppingCart$get_shoppingCartItems()
{
return this.shoppingCartItems;
}
add ShoppingCartItem
As you can see in Listing 5-15 , this method takes several steps to add the specified ShoppingCartItem
object to the
shoppingCartItems collection. First, it checks whether the shoppingCartItems collection
contains an object with the same
id as the object being added. If so, it throws a DuplicateItemException
(discussed in previous chapters). If not, it adds the specified
ShoppingCartItem to the

shoppingCartItems collection.
c05.indd 143c05.indd 143 8/20/07 6:01:52 PM8/20/07 6:01:52 PM
Chapter 5: Event Programming Extensions
144
Listing 5-15: The Shopping$ShoppingCart$addShoppingCartItem JavaScript Functions

function Shopping$ShoppingCart$addShoppingCartItem(shoppingCartItem)
{
var cartItems = this.get_shoppingCartItems();
var cartItemId = shoppingCartItem.get_id();

if (cartItems[cartItemId])
{
var exception = Error.duplicateItem(“Duplicate Shopping Cart Item!”,
{name: shoppingCartItem.get_name()});
throw exception;
}

else
this.shoppingCartItems[cartItemId] = shoppingCartItem;
}
Events
In this section, the functionality of the ShoppingCart class developed in the previous section is
extended to add support for events. You may be wondering why you need to enhance a class with
events. When you’re implementing a class, you do your best to ensure that your class provides its clients
with the necessary functionality. However, you cannot add application-specific functionality to your
class if you want different applications to use your class. This means that your class will not meet the
application-specific requirements of its clients.
Let’s take a look at some of the application-specific requirements that the version of the
ShoppingCart
class discussed in the previous section does not meet.
In Listing 5-13 , the
initialize method of the ShoppingCart class performed a single task — that is, it
instantiated the
shoppingCartItems collection that will contain the ShoppingCartItem objects added
to the current user’s shopping cart. There are several application-specific requirements that the current

implementation of the
initialize method does not meet, such as the following:
❑ As part of the initialization process, a typical shopping cart application also needs to populate
the
shoppingCartItems collection with the items that the current user selected in the previous
session. To do so, the application needs to run some application-specific code to retrieve the pre-
vious session’s items from the underlying data store.
❑ As part of the initialization process, a shopping cart application may also need to run some
application-specific code to perform certain filtering on the items that the current user selected
in the previous session.
As you’ll see later in this section, the
ShoppingCart class can be enhanced with an event named

ShoppingCartInitialized , which the initialize method can raise to allow the clients of the class
to execute application-specific initialization code.
In Listing 5-15 , the
addShoppingCartItem method of the ShoppingCart class added the specified

ShoppingCartItem object to the shoppingCartItems collection. Before adding the object to the collection,
c05.indd 144c05.indd 144 8/20/07 6:01:52 PM8/20/07 6:01:52 PM
Chapter 5: Event Programming Extensions
145
the shopping cart application may need to run some code that contains some application- specific logic to
determine whether the addition of the specified object would violate some application-specific rules.
As you’ll see later in this section, the
ShoppingCart class can be enhanced with a cancelable event
named
ShoppingCartItemAdding , which the addShoppingCartItem method can raise to allow the
clients of the class to cancel the
add operation if it violates application-specific rules.

In Listing 5-15 , the
addShoppingCartItem method raised a DuplicateItemException exception if the

shoppingCartItems already contains a ShoppingCartItem object with the same id value as the one
being added. Many applications prefer to use application-specific exception-handling mechanisms to
handle exceptions.
As you’ll see later, the ShoppingCart class can be enhanced with an event named ShoppingCart-
ItemAdded
, which the addShoppingCartItem method can raise to allow the clients of the class to use
application-specific exception-handling logic to handle the exception.
This event is useful even when no exception is raised because it allows the application to run
application-specific code after an item is added. For example, the application may want to display
information about a special promotion for the newly added item.
As you can see, enhancing your classes with events enables the clients of your classes to extend the
functionality of your classes to incorporate application-specific logic.
Listing 5-16 presents the new version of the
ShoppingCart.js JavaScript file that contains the imple-
mentation of all the classes of the shopping cart application. These classes are discussed in detail later in
this chapter.
Listing 5-16: The New Version of the ShoppingCart.js File
Type.registerNamespace(“Shopping”);

Shopping.ShoppingCartItem = function Shopping$ShoppingCartItem(id, name, price)
{
this.id = id;
this.name = name;
this.price = price;
}

function Shopping$ShoppingCartItem$get_id()

{
return this.id;
}

function Shopping$ShoppingCartItem$get_name()
{
return this.name;
}

(continued)
c05.indd 145c05.indd 145 8/20/07 6:01:52 PM8/20/07 6:01:52 PM
Chapter 5: Event Programming Extensions
146
Listing 5-16 (continued)
function Shopping$ShoppingCartItem$get_price()
{
return this.price;
}

Shopping.ShoppingCartItem.prototype = {
get_id : Shopping$ShoppingCartItem$get_id,
get_name : Shopping$ShoppingCartItem$get_name,
get_ price : Shopping$ShoppingCartItem$get_ price
};

Shopping.ShoppingCartItem.registerClass(“Shopping.ShoppingCartItem”);

Shopping.ShoppingCart = function() { }

function Shopping$ShoppingCart$get_events() {

if (!this.events)
this.events = new Sys.EventHandlerList();

return this.events;
}

function Shopping$ShoppingCart$initialize()
{
this.shoppingCartItems = {};
this.onShoppingCartInitialized(Sys.EventArgs.Empty);
}

function Shopping$ShoppingCart$onShoppingCartInitialized(e)
{
var handler = this.get_events().getHandler(“shoppingCartInitialized”);
if (handler)
handler(this, e);
}

function Shopping$ShoppingCart$addShoppingCartItem(shoppingCartItem)
{
var e1 = new Shopping.ShoppingCartItemAddingEventArgs(shoppingCartItem);
this.onShoppingCartItemAdding(e1);

if (!e1.get_cancel())
{
var exception = null;
var cartItems = this.get_shoppingCartItems();
var cartItemId = shoppingCartItem.get_id();


if (cartItems[cartItemId])
exception = Error.duplicateItem(“Duplicate Shopping Cart Item!”,
{name: shoppingCartItem.get_name()});
else
this.shoppingCartItems[cartItemId] = shoppingCartItem;

c05.indd 146c05.indd 146 8/20/07 6:01:53 PM8/20/07 6:01:53 PM
Chapter 5: Event Programming Extensions
147
var e2 =
new Shopping.ShoppingCartItemAddedEventArgs(shoppingCartItem, exception);
this.onShoppingCartItemAdded(e2);

if (!e2.get_exceptionHandled())
throw exception;
}
}

function Shopping$ShoppingCart$onShoppingCartItemAdding(e)
{
var handler = this.get_events().getHandler(“shoppingCartItemAdding”);
if (handler)
handler(this, e);
}

function Shopping$ShoppingCart$onShoppingCartItemAdded(e)
{
var handler = this.get_events().getHandler(“shoppingCartItemAdded”);
if (handler)
handler(this, e);

}

function Shopping$ShoppingCart$add_shoppingCartInitialized(handler)
{
this.get_events().addHandler(“shoppingCartInitialized”, handler);
}

function Shopping$ShoppingCart$add_shoppingCartItemAdding(handler)
{
this.get_events().addHandler(“shoppingCartItemAdding”, handler);
}

function Shopping$ShoppingCart$add_shoppingCartItemAdded(handler)
{
this.get_events().addHandler(“shoppingCartItemAdded”, handler);
}

function Shopping$ShoppingCart$remove_shoppingCartInitialized(handler)
{
this.get_events().removeHandler(“shoppingCartInitialized”, handler);
}

function Shopping$ShoppingCart$remove_shoppingCartItemAdding(handler)
{
this.get_events().removeHandler(“shoppingCartItemAdding”, handler);
}

function Shopping$ShoppingCart$remove_shoppingCartItemAdded(handler)
{
this.get_events().removeHandler(“shoppingCartItemAdded”, handler);

}

(continued)
c05.indd 147c05.indd 147 8/20/07 6:01:53 PM8/20/07 6:01:53 PM
Chapter 5: Event Programming Extensions
148
Listing 5-16 (continued)
function Shopping$ShoppingCart$get_shoppingCartItems()
{
return this.shoppingCartItems;
}

Shopping.ShoppingCart.prototype = {
addShoppingCartItem : Shopping$ShoppingCart$addShoppingCartItem,
initialize : Shopping$ShoppingCart$initialize,
get_shoppingCartItems : Shopping$ShoppingCart$get_shoppingCartItems,

get_events : Shopping$ShoppingCart$get_events,

add_shoppingCartInitialized :
Shopping$ShoppingCart$add_shoppingCartInitialized,
remove_shoppingCartInitialized :
Shopping$ShoppingCart$remove_shoppingCartInitialized,
onShoppingCartInitialized : Shopping$ShoppingCart$onShoppingCartInitialized,

add_shoppingCartItemAdding : Shopping$ShoppingCart$add_shoppingCartItemAdding,
remove_shoppingCartItemAdding:
Shopping$ShoppingCart$remove_shoppingCartItemAdding,
onShoppingCartItemAdding : Shopping$ShoppingCart$onShoppingCartItemAdding,


add_shoppingCartItemAdded : Shopping$ShoppingCart$add_shoppingCartItemAdded,
remove_shoppingCartItemAdded:
Shopping$ShoppingCart$remove_shoppingCartItemAdded,
onShoppingCartItemAdded : Shopping$ShoppingCart$onShoppingCartItemAdded
};

Shopping.ShoppingCart.registerClass(“Shopping.ShoppingCart”);

Shopping.ShoppingCartItemAddingEventArgs =
function Shopping$ShoppingCartItemAddingEventArgs (shoppingCartItem)
{
Shopping.ShoppingCartItemAddingEventArgs.initializeBase(this);
this.shoppingCartItem = shoppingCartItem;
}

function Shopping$ShoppingCartItemAddingEventArgs$get_shoppingCartItem()
{
return this.shoppingCartItem;
}

Shopping.ShoppingCartItemAddingEventArgs.prototype = {
get_shoppingCartItem :
Shopping$ShoppingCartItemAddingEventArgs$get_shoppingCartItem
};

c05.indd 148c05.indd 148 8/20/07 6:01:53 PM8/20/07 6:01:53 PM
Chapter 5: Event Programming Extensions
149
Shopping.ShoppingCartItemAddingEventArgs.registerClass(
“Shopping.ShoppingCartItemAddingEventArgs”, Sys.CancelEventArgs);


Shopping.ShoppingCartItemAddedEventArgs =
function Shopping$ShoppingCartItemAddedEventArgs (shoppingCartItem, exception)
{
Shopping.ShoppingCartItemAddedEventArgs.initializeBase(this);
this.shoppingCartItem = shoppingCartItem;
this.exception = exception;
this.exceptionHandled = false;
}

function Shopping$ShoppingCartItemAddedEventArgs$get_shoppingCartItem()
{
return this.shoppingCartItem;
}

function Shopping$ShoppingCartItemAddedEventArgs$get_exception()
{
return this.exception;
}

function Shopping$ShoppingCartItemAddedEventArgs$get_exceptionHandled()
{
return !this.exception || this.exceptionHandled;
}

function Shopping$ShoppingCartItemAddedEventArgs$set_exceptionHandled(value)
{
this.exceptionHandled = value;
}


Shopping.ShoppingCartItemAddedEventArgs.prototype = {
get_shoppingCartItem :
Shopping$ShoppingCartItemAddedEventArgs$get_shoppingCartItem,
get_exception : Shopping$ShoppingCartItemAddedEventArgs$get_exception,
get_exceptionHandled :
Shopping$ShoppingCartItemAddedEventArgs$get_exceptionHandled,
set_exceptionHandled :
Shopping$ShoppingCartItemAddedEventArgs$set_exceptionHandled
};

Shopping.ShoppingCartItemAddedEventArgs.registerClass(
“Shopping.ShoppingCartItemAddedEventArgs”, Sys.EventArgs);

if(typeof(Sys)!==’undefined’)
Sys.Application.notifyScriptLoaded();
c05.indd 149c05.indd 149 8/20/07 6:01:54 PM8/20/07 6:01:54 PM
Chapter 5: Event Programming Extensions
150
Listing 5-17 presents a page containing the new version of the shopping cart class that uses events.
Listing 5-17: A Page that uses the New Version of the Shopping Cart Class
<%@ 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 shoppingCartInitializedCallback(sender, e)
{
alert(“Shopping cart is initialized!”);

}

function shoppingCartItemAddingCallback(sender, e)
{
e.set_cancel(false);
alert(“Adding “ + e.get_shoppingCartItem().get_name());
}

function shoppingCartItemAddedCallback(sender, e)
{
alert(“Added “ + e.get_shoppingCartItem().get_name());
if (e.get_exception())
alert(e.get_exception());
}

function pageLoad()
{
var shoppingCart = new Shopping.ShoppingCart();
shoppingCart.add_shoppingCartInitialized(shoppingCartInitializedCallback);
shoppingCart.add_shoppingCartItemAdding(shoppingCartItemAddingCallback);
shoppingCart.add_shoppingCartItemAdded(shoppingCartItemAddedCallback);
shoppingCart.initialize();
var shoppingCartItem = new Shopping.ShoppingCartItem(1, “item1”, 23);
shoppingCart.addShoppingCartItem(shoppingCartItem);
shoppingCart.remove_shoppingCartInitialized(shoppingCartInitializedCallback);
shoppingCart.remove_shoppingCartItemAdding(shoppingCartItemAddingCallback);
shoppingCart.remove_shoppingCartItemAdded(shoppingCartItemAddedCallback);
}
</script>
</head>

c05.indd 150c05.indd 150 8/20/07 6:01:54 PM8/20/07 6:01:54 PM
Chapter 5: Event Programming Extensions
151
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScripManager1”>
<Scripts>
<asp:ScriptReference Path=”ShoppingCart.js” />
</Scripts>
</asp:ScriptManager>
</form>
</body>
</html>
As you can see, the pageLoad method instantiates a ShoppingCart object to represent the current user’s
shopping cart:
var shoppingCart = new Shopping.ShoppingCart();
Next, it calls the add_shoppingCartInitialized method on the ShoppingCart object to register a
JavaScript function named
shoppingCartInitializedCallback as an event handler for the

ShoppingCartInitialized event of the ShoppingCart object:
shoppingCart.add_shoppingCartInitialized(shoppingCartInitializedCallback);
The pageLoad method then calls the add_shoppingCartItemAdding method on the ShoppingCart
object to register a JavaScript function named
shoppingCartItemAddingCallback as the event
handler for the
ShoppingCartItemAdding event of the ShoppingCart object:
shoppingCart.add_shoppingCartItemAdding(shoppingCartItemAddingCallback);
Next, it calls the add_shoppingCartItemAdded method on the ShoppingCart object to register a
JavaScript function named

shoppingCartItemAddedCallback as the event handler for the

ShoppingCartItemAdded event of the ShoppingCart object:
shoppingCart.add_shoppingCartItemAdded(shoppingCartItemAddedCallback);
Then, it calls the initialize method on the ShoppingCart object to initialize the object:
shoppingCart.initialize();
Next, it instantiates a ShoppingCartItem object with the specified id , name , and price , and calls the

addShoppingCartItem method on the ShoppingCart object, passing in the ShoppingCartItem object
to add the object to
shoppingCartItems :
var shoppingCartItem = new Shopping.ShoppingCartItem(1, “item1”, 23);
shoppingCart.addShoppingCartItem(shoppingCartItem);
c05.indd 151c05.indd 151 8/20/07 6:01:54 PM8/20/07 6:01:54 PM
Chapter 5: Event Programming Extensions
152
Finally, it calls the associated remove methods on the ShoppingCard object to remove the JavaScript
functions that were previously registered:
shoppingCart.remove_shoppingCartInitialized(shoppingCartInitializedCallback);
shoppingCart.remove_shoppingCartItemAdding(shoppingCartItemAddingCallback);
shoppingCart.remove_shoppingCartItemAdded(shoppingCartItemAddedCallback);
You’ll understand the implementation of the shoppingCartInitializedCallback ,
shoppingCartItemAddingCallback , and shoppingCartItemAddedCallback event handlers better
when the events of the
ShoppingCart class are discussed later in this chapter.
ShoppingCartItemAddingEventArgs
This class is the event data class for the ShoppingCartItemAdding event. As you can see in Listing 5-18 ,
this class exposes a getter method named
get_shoppingCartItem that returns a reference to the


ShoppingCartItem object being added.
Note that the
ShoppingCartItemAddingEventArgs event data class derives from the
Sys.CancelEventArgs class, which is the base event data class for all cancelable events. As discussed
earlier, the
Sys.CancelEventArgs class features two important methods named get_cancel and
set_cancel that allow an event handler for a cancelable event to cancel the event.
As such, the
ShoppingCartItemAddingEventArgs event data class inherits the get_cancel and
set_cancel methods from its base class.
Listing 5-18: The ShoppingCartItemAddingEventArgs Event Data Class
Shopping.ShoppingCartItemAddingEventArgs =
function Shopping$ShoppingCartItemAddingEventArgs (shoppingCartItem)
{
Shopping.ShoppingCartItemAddingEventArgs.initializeBase(this);
this.shoppingCartItem = shoppingCartItem;
}

function Shopping$ShoppingCartItemAddingEventArgs$get_shoppingCartItem()
{
return this.shoppingCartItem;
}

Shopping.ShoppingCartItemAddingEventArgs.prototype = {
get_shoppingCartItem :
Shopping$ShoppingCartItemAddingEventArgs$get_shoppingCartItem
};

Shopping.ShoppingCartItemAddingEventArgs.registerClass(
“Shopping.ShoppingCartItemAddingEventArgs”, Sys.CancelEventArgs);

c05.indd 152c05.indd 152 8/20/07 6:01:55 PM8/20/07 6:01:55 PM
Chapter 5: Event Programming Extensions
153
ShoppingCartItemAddedEventArgs
This class acts as the event data class for the ShoppingCartItemAdded event as shown in Listing 5-19 .
Listing 5-19: The ShoppingCartItemAddedEventArgs Event Data Class
Shopping.ShoppingCartItemAddedEventArgs =
function Shopping$ShoppingCartItemAddedEventArgs (shoppingCartItem, exception)
{
Shopping.ShoppingCartItemAddedEventArgs.initializeBase(this);
this.shoppingCartItem = shoppingCartItem;
this.exception = exception;
this.exceptionHandled = false;
}

function Shopping$ShoppingCartItemAddedEventArgs$get_shoppingCartItem()
{
return this.shoppingCartItem;
}

function Shopping$ShoppingCartItemAddedEventArgs$get_exception()
{
return this.exception;
}

function Shopping$ShoppingCartItemAddedEventArgs$get_exceptionHandled()
{
return !this.exception || this.exceptionHandled;
}


function Shopping$ShoppingCartItemAddedEventArgs$set_exceptionHandled(value)
{
this.exceptionHandled = value;
}

Shopping.ShoppingCartItemAddedEventArgs.prototype = {
get_shoppingCartItem :
Shopping$ShoppingCartItemAddedEventArgs$get_shoppingCartItem,
get_exception : Shopping$ShoppingCartItemAddedEventArgs$get_exception,
get_exceptionHandled :
Shopping$ShoppingCartItemAddedEventArgs$get_exceptionHandled,
set_exceptionHandled :
Shopping$ShoppingCartItemAddedEventArgs$set_exceptionHandled
};

Shopping.ShoppingCartItemAddedEventArgs.registerClass(
“Shopping.ShoppingCartItemAddedEventArgs”, Sys.EventArgs);
c05.indd 153c05.indd 153 8/20/07 6:01:55 PM8/20/07 6:01:55 PM
Chapter 5: Event Programming Extensions
154
As you can see in this listing, the ShoppingCartItemAddedEventArgs class exposes the following four
methods:
❑ get_shoppingCartItem : This getter returns a reference to the ShoppingCartItem object that
was added to the
shoppingCartItems of the ShoppingCart object that represents the current
user’s shopping cart.
❑ get_exception : This getter returns a reference to the Exception object raised during the
execution of the
addShoppingCartItem method of the ShoppingCart object. An event handler
can call this getter to access the

Exception object and use application-specific exception-
handling logic to handle the exception.
❑ set_exceptionHandled : This setter allows an event handler to inform the addShoppingCart-
Item
method of the ShoppingCart object to bypass the default exception-handling logic because
the exception has already been handled by application-specific exception-handling logic.
❑ get_exceptionHandled : The addShoppingCartItem method calls this getter to find out if the
event handler has already handled the exception.
ShoppingCart
As you can see in Listing 5-20 , the ShoppingCart.js JavaScript file defines the ShoppingCart class
whose instances represent user shopping carts. The methods of this class are discussed in the following
sections.
Listing 5-20: The ShoppingCart.js JavaScript File
Shopping.ShoppingCart = function() { }

Shopping.ShoppingCart.prototype = {
addShoppingCartItem : Shopping$ShoppingCart$addShoppingCartItem,
initialize : Shopping$ShoppingCart$initialize,
get_shoppingCartItems : Shopping$ShoppingCart$get_shoppingCartItems,

get_events : Shopping$ShoppingCart$get_events,

add_shoppingCartInitialized :
Shopping$ShoppingCart$add_shoppingCartInitialized,
remove_shoppingCartInitialized :
Shopping$ShoppingCart$remove_shoppingCartInitialized,
onShoppingCartInitialized : Shopping$ShoppingCart$onShoppingCartInitialized,

add_shoppingCartItemAdding : Shopping$ShoppingCart$add_shoppingCartItemAdding,
remove_shoppingCartItemAdding:

Shopping$ShoppingCart$remove_shoppingCartItemAdding,
onShoppingCartItemAdding : Shopping$ShoppingCart$onShoppingCartItemAdding,

add_shoppingCartItemAdded : Shopping$ShoppingCart$add_shoppingCartItemAdded,
c05.indd 154c05.indd 154 8/20/07 6:01:55 PM8/20/07 6:01:55 PM
Chapter 5: Event Programming Extensions
155
remove_shoppingCartItemAdded:
Shopping$ShoppingCart$remove_shoppingCartItemAdded,
onShoppingCartItemAdded : Shopping$ShoppingCart$onShoppingCartItemAdded
};

Shopping.ShoppingCart.registerClass(“Shopping.ShoppingCart”);
get _ events
As discussed earlier, the ASP.NET AJAX client-side framework exposes a class named Sys.EventHan-
dlerList
that emulates the .NET System.ComponentModel.EventHandlerList class. As you can see
in Listing 5-21 , the
ShoppingCart class exposes a getter named get_events whose main responsibility
is to instantiate the
Sys.EventHandlerList class if it hasn’t already been instantiated and to return the
instance to its caller.
Listing 5-21: The get_events Method of the ShoppingCart Class
function Shopping$ShoppingCart$get_events() {
if (!this.events)
this.events = new Sys.EventHandlerList();

return this.events;
}
initialize

Listing 5-22 contains the code for the initialize method of the ShoppingCart class.
Listing 5-22: The initialize Method of the ShoppingCart Class
function Shopping$ShoppingCart$initialize()
{
this.shoppingCartItems = {};
this.onShoppingCartInitialized(Sys.EventArgs.Empty);
}
As you can see in this listing, the initialize method of the ShoppingCart class performs two impor-
tant tasks. First, it instantiates the
shoppingCartItems where the ShoppingCartItem objects will be
stored. Second, it calls the
onShoppingCartInitialized method of the ShoppingCart object, passing
in the
Sys.EventArgs.Empty parameter to raise the ShoppingCartInitialized event. This event
does not involve any event data, so it uses the
Sys.EventArgs base class as its event data class. This is
very similar to .NET, where the
System.EventArgs base class is used as the event data class for events
that do not involve any event data. The
Sys.EventArgs.Empty provides the same programming conve-
nience as its .NET counterpart — that is, the
System.EventArgs.Empty .
on ShoppingCartInitialized
Listing 5-23 presents the implementation of the onShoppingCartInitialized method of the

ShoppingCart class.
c05.indd 155c05.indd 155 8/20/07 6:01:56 PM8/20/07 6:01:56 PM

×