Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 30 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
30
Dung lượng
142,72 KB
Nội dung
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” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml”> <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 [...]... programming convenience as its NET counterpart — that is, the System.EventArgs.Empty onShoppingCartInitialized Listing 5- 2 3 presents the implementation of the onShoppingCartInitialized method of the ShoppingCart class 155 c 05. indd 155 8/20/07 6:01 :56 PM Chapter 5: Event Programming Extensions Listing 5- 2 3: The onShoppingCartInitialized Method of the ShoppingCart Class function Shopping$ShoppingCart$onShoppingCartInitialized(e)... any) only if this field has been set to true: if (!e2.get_exceptionHandled()) throw exception; 157 c 05. indd 157 8/20/07 6:01 :56 PM Chapter 5: Event Programming Extensions onShoppingCartItemAdding Listing 5- 2 5 shows the implementation of the onShoppingCartItemAdding method of the ShoppingCart class Listing 5- 2 5: The onShoppingCartItemAdding Method of the ShoppingCart Class function Shopping$ShoppingCart$onShoppingCartItemAdding(e)... Summar y The ASP.NET AJAX event-programming extensions emulate the NET event-programming paradigm This enables you to use a programming model very similar to the NET event-programming model to add events to your JavaScript classes The next chapter discusses Document Object Model (DOM) event programming — a common client-side event programming practice 159 c 05. indd 159 8/20/07 6:01 :57 PM c 05. indd 160... Shopping.ShoppingCartItemAddingEventArgs.registerClass( “Shopping.ShoppingCartItemAddingEventArgs”, Sys.CancelEventArgs); 152 c 05. indd 152 8/20/07 6:01 :55 PM Chapter 5: Event Programming Extensions ShoppingCartItemAddedEventArgs This class acts as the event data class for the ShoppingCartItemAdded event as shown in Listing 5- 1 9 Listing 5- 1 9: The ShoppingCartItemAddedEventArgs Event Data Class Shopping.ShoppingCartItemAddedEventArgs... Shopping$ShoppingCart$add_shoppingCartItemAdded, 154 c 05. indd 154 8/20/07 6:01 :55 PM Chapter 5: Event Programming Extensions 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... Sys.Application.notifyScriptLoaded(); 149 c 05. indd 149 8/20/07 6:01 :54 PM Chapter 5: Event Programming Extensions Listing 5- 1 7 presents a page containing the new version of the shopping cart class that uses events Listing 5- 1 7: A Page that uses the New Version of the Shopping Cart Class ... this.price = price; } function Shopping$ShoppingCartItem$get_id() { return this.id; } function Shopping$ShoppingCartItem$get_name() { return this.name; } (continued) 1 45 c 05. indd 1 45 8/20/07 6:01 :52 PM Chapter 5: Event Programming Extensions Listing 5- 1 6 (continued) function Shopping$ShoppingCartItem$get_price() { return this.price; } Shopping.ShoppingCartItem.prototype = { get_id : Shopping$ShoppingCartItem$get_id,... Shopping.ShoppingCartItemAddedEventArgs.registerClass( “Shopping.ShoppingCartItemAddedEventArgs”, Sys.EventArgs); 153 c 05. indd 153 8/20/07 6:01 :55 PM Chapter 5: Event Programming Extensions 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... ShoppingCartItemAdding, and ShoppingCartItemAdded events, respectively Notice that each add method delegates to the addHandler method of the internal EventHandlerList object 158 c 05. indd 158 8/20/07 6:01 :56 PM Chapter 5: Event Programming Extensions Listing 5- 2 7: The Methods of the ShoppingCart Class that Add Event Handlers function Shopping$ShoppingCart$add_shoppingCartInitialized(handler) { this.get_events().addHandler(“shoppingCartInitialized”,... Shopping$ShoppingCartItem$get_price() { return this.price; } ShoppingCart Listing 5- 1 2 shows the implementation of the ShoppingCart class Listing 5- 1 2: The ShoppingCart Class Shopping.ShoppingCart = function() { } Shopping.ShoppingCart.prototype = { addShoppingCartItem : Shopping$ShoppingCart$addShoppingCartItem, (continued) 142 c 05. indd 142 8/20/07 6:01 :51 PM Chapter 5: Event Programming Extensions initialize : Shopping$ShoppingCart$initialize, . 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. c 05. indd 136c 05. indd 136 8/20/07 6:01 :50 . Sys.Application.notifyScriptLoaded(); c 05. indd 139c 05. 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. Shopping$ShoppingCartItem$get_name() { return this.name; } (continued) c 05. indd 145c 05. indd 1 45 8/20/07 6:01 :52 PM8/20/07 6:01 :52 PM Chapter 5: Event Programming Extensions 146 Listing 5- 1 6 (continued) function Shopping$ShoppingCartItem$get_price() {