If so, the constructor raises an exception: if typeofelement.control != ‘undefined’ throw Error.invalidOperationSys.Res.controlAlreadyDefined; Note that the constructor calls the initia
Trang 1Developing Client Controls
As discussed in the previous chapter, the Component class is the base class for all ASP.NET AJAX components The ASP.NET AJAX client-side framework includes two important subclasses of the Component base class: Sys.UI.Control and Sys.UI.Behavior Therefore, when it comes
to choosing a base class from which to derive your component class, you have three options:
Component , Control , and Behavior The previous chapter showed you how to implement an ASP.NET AJAX component that derives from the Component base class This chapter first provides you with in-depth coverage of the Control class and its methods, properties, and events Then it provides you with a recipe for developing ASP.NET AJAX components that derive from the
Control class Finally, it uses this recipe to implement a custom control class
Listing 8-1 presents the definition of the Control class Note that this code listing registers the
Control class as the subclass of the Component base class:
Sys.UI.Control.registerClass(‘Sys.UI.Control’, Sys.Component);
The Control class exposes several methods and properties, which are discussed in the following sections This section discusses the constructor of the class
Trang 2Listing 8-1: The Definition of the Control Class
Sys.UI.Control = function Sys$UI$Control(element) {
As you can see, the constructor of the Control class takes a single argument that references the DOM
element that the Control instance being instantiated will represent You can think of the Control
instance as the ASP.NET AJAX representation of the DOM element Consequently, the DOM element that
the Control instance is supposed to represent must already exist in the document where the Control
instance is instantiated
Notice that the constructor assigns the newly instantiated Control instance to the control property of
the DOM element, signifying that the DOM element knows which ASP.NET AJAX Control object
repre-sents it:
element.control = this;
Trang 3As a result, every DOM element can be represented by only one Control object To enforce this requirement, the constructor first checks whether the control property of the specified DOM element already references an object If so, the constructor raises an exception:
if (typeof(element.control) != ‘undefined’) throw Error.invalidOperation(Sys.Res.controlAlreadyDefined);
Note that the constructor calls the initializeBase method, passing in the reference to the Control instance being instantiated to invoke the constructor of its base class, which is the Component base class:
Sys.UI.Control.initializeBase(this);
The constructor stores the DOM element passed into it in a field named _element :
this._element = element;
The constructor then stores the value of the display property of the style property of the DOM element
in another field named _oldDisplayMode :
Trang 4Listing 8-3: The get_id Method of the Control Class
Because the value of a Control object’s id property is the same as the value of the id HTML attribute of
the DOM element that the Control object represents, the id property of the Control object cannot be
set Therefore, the Control class overrides the set_id method that it inherits from the Component base
class to raise an InvalidOperation exception This exception informs the client of a Control object
that calls this method that setting the value of the id property of the Control object is an invalid
opera-tion, as shown in Listing 8-4
Listing 8-4: The set_id Method of the Control Class
function Sys$UI$Control$set_id(value) {
throw Error.invalidOperation(Sys.Res.cantSetId);
}
set_parent
The Control class exposes a property named parent that references the parent Control object of a
Control object The Control class features a method named set_parent that allows you to specify
another Control object as the parent of the Control object on which this method is invoked, as shown
in Listing 8-5
Listing 8-5: The set_parent Method of the Control Class
function Sys$UI$Control$set_parent(value) {
var parents = [this];
var current = value;
Trang 5get_parent
The Control class exposes a method named get_parent that returns the parent Control object of a
Control object on which this method is invoked, as shown in Listing 8-6 Listing 8-6: The get_parent Method of the Control Class function Sys$UI$Control$get_parent() {
if (this._parent) return this._parent;
else { var parentElement = this._element.parentNode;
while (parentElement) {
if (parentElement.control) return parentElement.control;
parentElement = parentElement.parentNode;
} return null;
}}
As you can see in this listing, the get_parent method returns the value of the _parent property of the
Control object on which the method is invoked if the value of this property has been set:
if (this._ parent) return this._ parent;
However, if the value of the _ parent property of the Control object has not been specified, the
get_parent method searches upward through the containment hierarchy of the DOM element that the
Control object represents for the first DOM element whose control property has been specified and returns the value of this control property as the parent Control object As previously shown in Listing 8-2 , the value of a control property of a DOM element references the Control object that represents the DOM element
Therefore, if the value of the parent property of the Control object that represents a DOM element is not explicitly specified, the Control object that represents the first parent DOM element in the contain-ment hierarchy of the DOM element will be used as the parent Control object of the DOM element
As Listing 8-6 shows, if the parent property of the Control object that represents a DOM element is not specified, and no parent DOM element in the containment hierarchy of the DOM element is repre-sented by a Control object, the get_parent method returns null This means that it is possible to have
a Control object without a parent
Trang 6get_visibilityMode
The Control class exposes a property of type VisibilityMode named visibilityMode Listing 8-7
presents the definition of the VisibilityMode type As you can see, the VisibilityMode is an
enu-meration with two possible values: hide and collapse
Listing 8-7: The VisibilityMode Type
Sys.UI.VisibilityMode = function Sys$UI$VisibilityMode() {
The get_visibilityMode method of the Control class returns the value of the visibilityMode
property of the Control , as shown in Listing 8-8
Listing 8-8: The get_visibilityMode Method of the Control Class
function Sys$UI$Control$get_visibilityMode() {
return this._visibilityMode;
}
get_visible
The Control class contains a method named get_visible that returns the visibility status of the DOM
element that the current Control object represents, as shown in Listing 8-9 In other words, the visibility
status of a Control object is same as the visibility status of the DOM element that the Control object
The set_visibilityMode method of the Control class enables you to set the value of the
visibili-tyMode property of the Control object on which this method is invoked, as shown in Listing 8-10 Due
to the fact that a Control object is an ASP.NET AJAX representation of a DOM element, setting its
prop-erties affects the DOM element that it represents In this case, setting the visibilityMode property of a
Trang 7Control object changes the value of the display property of the DOM element’s style property if the DOM element is invisible More specifically, if the visibilityMode property is set to the enumeration value VisibilityMode.hide , the display property reverts to its original value The constructor of the
Control class stores the original value of the display property of the DOM element’s style property
in a field named _oldDisplayMode If the visibilityMode property is set to the enumeration value
VisibilityMode.collapse , the display property of the DOM element’s style property is set to none
Listing 8-10: The set_visibilityMode Method of the Control Class function Sys$UI$Control$set_visibilityMode(value)
{
if (this._visibilityMode !== value) {
this._visibilityMode = value;
if (this.get_visible() === false) {
if (this._visibilityMode === Sys.UI.VisibilityMode.hide) this._element.style.display = this._oldDisplayMode;
else this._element.style.display = ’none’;
} } this._visibilityMode = value;
Listing 8-11: The set_visible Method of the Control Class function Sys$UI$Control$set_visible(value) {
if (value != this.get_visible()) {
this._element.style.visibility = value ? ‘visible’ : ‘hidden’;
if (value || (this._visibilityMode === Sys.UI.VisibilityMode.hide)) this._element.style.display = this._oldDisplayMode;
else this._element.style.display=’none’;
}}
Trang 8add CssClass
When this method is invoked on a Control object, it calls the addCssClass static method on the
DomElement class to add the specified CSS class to the DOM element that the Control object represents,
When this method is invoked on a Control object, it calls the removeCssClass static method on the
DomElement class to remove the specified CSS class from the DOM element that the Control object
represents, as shown in Listing 8-13
Listing 8-13: The remove CssClass Method of the Control Class
function Sys$UI$Control$removeCssClass(className) {
Sys.UI.DomElement.removeCssClass(this._element, className);
}
toggle CssClass
When this method is called on a Control object, it calls the toggleCssClass static method on the
DomElement class to toggle the specified CSS class of the DOM element that the Control object
repre-sents, as shown in Listing 8-14 What this means is that if the DOM element already contains the
speci-fied CSS class, the toggleCssClass method removes the CSS class Otherwise, the method adds the
CSS class to the DOM element
Listing 8-14: The toggle CssClass Method of the Control Class
function Sys$UI$Control$toggleCssClass(className) {
Sys.UI.DomElement.toggleCssClass(this._element, className);
}
dispose
The Control class overrides the dispose method that it inherits from the Component base class, as
shown in Listing 8-15 This method calls the delete method on the element property that references
the DOM element that the current Control object represents
Trang 9Listing 8-15: The dispose Method of the Control Class function Sys$UI$Control$dispose() {
Sys.UI.Control.callBaseMethod(this, ‘dispose’);
if (this._element) {
this._element.control = undefined;
delete this._element;
}}
on BubbleEvent
The Control base class in the ASP.NET Framework exposes a method named OnBubbleEvent that its subclasses can override to catch the events that their child controls bubble up For example, the
GridViewRow class overrides the OnBubbleEvent method to catch the Command events that its child Image, Button, or Link controls bubble up
The ASP.NET AJAX Control base class exposes a method named onBubbleEvent that emulates the
OnBubbleEvent method of the ASP.NET Control base class This means that your custom client control can override this method to catch the events that its child Control objects bubble up, as shown in Listing 8-16
Listing 8-16: The on BubbleEvent Method of the Control Class function Sys$UI$Control$onBubbleEvent(source, args) { return false;
}
As the listing shows, the onBubbleEvent method takes two arguments and returns a Boolean value The first argument references the child Control object that bubbled up the event The second argument is of type EventArgs As mentioned, the OnBubbleEvent method allows your custom client control to catch the events that its child controls bubble up What your custom client control does with the events that
it catches is up to your custom control Normally, your custom client control is only interested in certain types of events It’s the responsibility of your custom client control’s implementation of the
onBubbleEvent method to use the second argument of the method to determine the type of the event
If the event is not of the type that your custom control is interested in, your custom control’s tation of the method must return false to allow the event to bubble further up in the containment hier-archy of your control However, if the event is indeed of the type that your custom control is interested
implemen-in, your custom control must return true to stop the event from bubbling further up the containment hierarchy (as shown later in this chapter)
In Listing 8-16 , the onBubbleEvent method of the Control base class returns false to allow the event
to bubble further up in the containment hierarchy
Trang 10raise BubbleEvent
The ASP.NET Control base class exposes a method named RaiseBubbleEvent that its subclasses can
invoke to bubble up their events For example, the GridViewRow control calls this method to bubble its
events up to the containing GridView control, where the GridView control catches these events in its
OnBubbleEvent method
The ASP.NET AJAX Control base class exposes a method named raiseBubbleEvent that emulates the
RaiseBubbleEvent method of the ASP.NET Control base class Your custom client control can call this
method to bubble its events up to its containing controls You’ll see an example of this later in this
chapter
Now let’s take a look at the internal implementation of the Control base class’s raiseBubbleEvent
method, which is shown in Listing 8-17
Listing 8-17: The raise BubbleEvent Method of the Control Class
function Sys$UI$Control$raiseBubbleEvent(source, args) {
var currentTarget = this.get_ parent();
As you can see, this 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 The onBubbleEvent
method of a client control returns true when it catches an event that it can handle
Developing Custom Client Controls
An ASP.NET AJAX client control is an ASP.NET AJAX client component that directly or indirectly
derives from the Control base class You can think of an ASP.NET AJAX client control as an ASP.NET
AJAX representation of a specific DOM element on a page
The ASP.NET AJAX client controls essentially emulate their corresponding ASP.NET server controls
Most basic ASP.NET server controls, such as Label and Image , are ASP.NET representations of DOM
elements These representations enable you to program against the underlying DOM elements using the
ASP.NET/.NET Framework In other words, these representations enable you to treat DOM elements as
.NET objects
The ASP.NET AJAX client controls play a similar role in the client-side programming These controls are
the ASP.NET AJAX representations of DOM elements, allowing you to program against these elements
using the ASP.NET AJAX Framework In other words, these representations enable you to treat DOM
elements as ASP.NET AJAX objects
Trang 11Every ASP.NET AJAX client control emulates its corresponding ASP.NET server control as much as possible As such, they expose similar methods and properties as their server counterparts
The ASP.NET AJAX client-side framework includes with a Sys.Preview namespace defined as follows:
Label Client Control
The ASP.NET AJAX Label client control is the ASP.NET AJAX representation of the <span> HTML ment The Label client control derives from the Control base class and extends its functionality to add support for two new properties named htmlEncode and text The following sections discuss the mem-bers of the Label client control
Constructor
Listing 8-18 presents the implementation of the constructor of the Label client control Note that this constructor takes a single argument, which references the DOM span element that the Label control represents
Listing 8-18: The Constructor of the Label Client Control Sys.Preview.UI.Label = function Sys$Preview$UI$Label(associatedElement){
Sys.Preview.UI.Label.initializeBase(this, [associatedElement]);
}Sys.Preview.UI.Label.registerClass(‘Sys.Preview.UI.Label’,Sys.UI.Control);
This constructor calls the initializeBase method to invoke the constructor of its base class—
Control —passing in the reference to the DOM element that the Label control represents
html Encode
The Label client control exposes a getter method named get_htmlEncode and a setter method named
set_htmlEncode that respectively get and set the value of the htmlEncode Boolean property of the trol, as shown in Listing 8-19
Trang 12Listing 8-19: The Getter and Setter Methods of the html Encode Property
Listing 8-20 presents the implementation of the Label control’s get_text getter method, which returns
the value of the text property of the control
Listing 8-20: The get_text Getter Method of the Label Control
This method first calls the get_element method to return a reference to the DOM element that the
Label control represents:
var element = this.get_element();
The Label control inherits the get_element method from its base class— Control
Next, the get_text method checks whether the value of the htmlEncode property is set to true If so, it
returns the value of the innerText property of the DOM element that the Label control represents:
Trang 13Listing 8-21: The set_text Method of the Label Control function Sys$Preview$UI$Label$set_text(value){
if (!value) value=””;
var element = this.get_element();
if (this._htmlEncode) {
if (element.innerText !== value) {
element.innerText = value;
this.raisePropertyChanged(‘text’);
} } else {
if (element.innerHTML !== value) {
element.innerHTML = value;
this.raisePropertyChanged(‘text’);
} }}
This method first calls the get_element method of its base class to return a reference to the DOM element that the Label control represents:
var element = this.get_element();
Next, it checks whether the value of the Label control’s htmlEncode property has been set to true
If so, it assigns the new value to the innerText property of the DOM element and calls the
raisePropertyChanged method to raise the propertyChanged event:
element.innerText = value;
this.raisePropertyChanged(‘text’);
The Label control inherits the raisePropertyChanged method from the Component base class
If the htmlEncode property has been set to false, get_text assigns the new value to the innerHTML property of the DOM element and calls the raisePropertyChanged method to raise the
propertyChanged event
The get_text and set_text methods of the Label control constitute convenient wrappers around the
innerText and innerHTML properties of the DOM element that the control represents
If you’re wondering how the get_text and set_text methods work in a browser such as Firefox that does not support the innerText property, the answer lies in the Mozilla compatibility layer of the ASP.NET AJAX client-side framework, which includes the logic that adds the support for this property Refer
to the PreviewScripts.js JavaScript file for more information on the Mozilla compatibility layer
Trang 14prototype
As Listing 8-22 shows, the get_htmlEncode , set_htmlEncode , get_text , and set_text methods of
the Label client control are directly defined on the prototype property of the control This means that
these methods are instance methods and must be invoked on the instances of the Label control class, not
the class itself
Listing 8-22: The prototype Property of the Label Control
Every component, including the Label control, must expose a property named descriptor that
refer-ences an object literal describing the members of the component The ASP.NET AJAX client-side
frame-work includes a class named TypeDescriptor that uses the descriptor property of a component to
discover its members In other words, the descriptor property of a component contains metadata
about the type of the component and its members As such, the descriptor property of a component
must always be defined directly on the component class itself
The descriptor property of a component references an object literal that contains one or more name/
value pairs, where each name/value pair describes a specific group of members The name part of the
name/value pair that describes the properties of a component contains the word properties , and the
value part is an array of object literals where each object literal describes a particular property In the case
of the Label control, this array contains two object literals, where the first object literal describes the
htmlEncode property and the second object literal describes the text property (see Listing 8-23 ) Each
object literal contains two name/value pairs The name part of the first name/value pair is the word
name , and the value part is the string that contains the name of the property being described The name
part of the second name/value pair is the word type , and the value part references the constructor of the
type of the property being described
Listing 8-23: The descriptor Property of the Label Control
Sys.Preview.UI.Label.descriptor =
{
properties: [ { name: ‘htmlEncode’, type: Boolean },
{ name: ‘text’, type: String } ]
}
Trang 15Using Label Client Control
Listing 8-24 presents a page that uses the Label client control
Listing 8-24: A Page that Uses the Label Client Control
var btn = $get(“btn”);
$addHandler(btn, “click”, clickcb);
label = $create(Sys.Preview.UI.Label, null, null, null, $get(“myspan”));
} </script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<input type=”checkbox” id=”chkbx”/>
<label for=”chkbx”>Enable HTML encoding</label>
<br /><br />
Enter text: <input type=”text” id=”txtbx” />
<button id=”btn” type=”button”>Submit</button><br /><br />
Trang 16Figure 8-1 shows what you’ll see in your browser when you access this page:
❑ A check box that allows you to toggle HTML encoding on or off
❑ A text box where you can enter text
❑ A Submit button
❑ A <span> HTML element
Figure 8-1 When you enter a text into the text box and click the Submit button, the callback for the button retrieves
the text and displays it inside the <span> HTML element Figure 8-2 presents a different scenario from
what’s shown in Figure 8-1 The text “ <b>ASP.NET AJAX </b>” is entered in both cases, containing the
opening and closing tags of the <b> HTML element In Figure 8-1 , however, the HTML encoding is off
In this case, the opening and closing tags of the <b> HTML element are not HTML-encoded and
conse-quently the <span> HTML element shows the text in bold In Figure 8-2 , on the other hand, the HTML
encoding is on In this case, the opening and closing tags of the <b> element are HTML encoded and
consequently the <span> element displays these tags as if they were normal non-HTML characters
Trang 17Note that the page shown in Listing 8-24 contains the following reference:
<asp:ScriptReference Assembly=”Microsoft.Web.Preview”
Path=”PrevewScript.js” />
This script references a JavaScript file named PreviewScripts.js that contains the definition of the
Label client control This JavaScript file is embedded in the Microsoft.Web.Preview.dll assembly You need to add this assembly to the bin directory of your application When you install the Microsoft ASP.NET Futures, it automatically adds the necessary template to the Visual Studio Therefore, if you use this template when you’re creating a new Web site, the Microsoft.Web.Preview.dll assembly will be automatically added to the bin directory of your Web site
As Listing 8-24 shows, pageLoad calls the addHandler static method on the DomEvent class to register the clickcb JavaScript function as the event handler for the click event of the Submit button:
var btn = $get(“btn”);
$addHandler(btn, “click”, clickcb);
Then pageLoad instantiates an instance of the Label client control to represent the <span> HTML element:
label = $create(Sys.Preview.UI.Label, null, null, null, $get(“myspan”));
Now let’s walk through the code for the clickcb JavaScript function This function first uses the $get global JavaScript function to return a reference to the check box element:
Image Client Control
The ASP.NET Image server control is the ASP.NET representation of an image DOM element As such, it exposes the width , height , src , and alt properties of this DOM element as the Width , Height ,
ImageURL , and AlternateText properties on the Image server control itself This allows you to treat these DOM properties as properties on the Image server-control.NET object
The ASP.NET AJAX Image client control plays the same role in the ASP.NET AJAX client-side work It is the ASP.NET AJAX representation of an image DOM element As such, it exposes the DOM
frame-width , height , src , and alt properties of this DOM element as the width , height , imageURL , and
Trang 18alterateText properties on the Image client control itself This allows you to treat these DOM
proper-ties as properproper-ties on an ASP.NET AJAX Image client control object The following sections discuss the
implementation of the Image client control members
Constructor
As Listing 8-25 shows, the constructor of the Image client control takes a single argument, which
references the <img> HTML element the Image client control will represent This constructor simply
calls the initializeBase method to invoke the constructor of its Control base class, passing in the
reference to the <img> element The registerClass method is then called to register the Image class as
the subclass of the Control base class
Listing 8-25: The Constructor of the Image Client Control
Listing 8-26 presents the implementation of the prototype property of the Image client control In this
implementation, an object literal describing all the instance methods of the control has been assigned to
the prototype property As discussed in the previous chapter, an instance method of a class is a method
that is directly defined on the prototype property of the class, as opposed to the class itself An instance
method must always be invoked on an instance of a class, not the class itself
Listing 8-26: The prototype Property of the Image Client Control
As you can see in this listing, the Image client control exposes four pairs of instance methods Each pair
allows you to set and get the value of a particular property of the Image class For example, the
set_height and get_height instance methods allow you to set and get the value of the height
property of the Image client control
The four properties that the Image client control exposes — width , height , imageURL , and
alternateText — are given the same names as the corresponding properties of its Image server control
Trang 19Listing 8-27: The set_imageURL and get_imageURL Methods of the Image Client Control
function Sys$Preview$UI$Image$get_imageURL() {
return this.get_element().src;
} function Sys$Preview$UI$Image$set_imageURL(value) {
Listing 8-28: The set_width and get_width Methods of the Image Client Control function Sys$Preview$UI$Image$get_width()
{ return this.get_element().width;
} function Sys$Preview$UI$Image$set_width(value) {
this.get_element().width = value;
}
height
As you can see in Listing 8-29 , the set_height and get_height methods act as wrappers around the
height property of the underlying image DOM element This enables you to treat this as a property on
an ASP.NET AJAX object, which is the Image client control in this case
Listing 8-29: The set_height and get_height Methods of the Image Client Control function Sys$Preview$UI$Image$get_height()
{ return this.get_element().height;
(continued)
Trang 20The get_alternateText and set_alternateText methods allow you to get and set the value of the
alt property of the image DOM element using the ASP.NET AJAX client-side framework in the same
way as you would to get and set the value of this property using the ASP.NET Framework (see Listing 8-30 )
Listing 8-30: The set_alternateText and get_alternateText Methods of the Image Client
Using the Image Client Control
Listing 8-31 presents a page that uses the Image client control Previously, we implemented a similar page
that showed how to use the Label client control where the page used the following script reference to
reference the PreviewScript.js JavaScript file embedded in the Microsoft.Web.PreviewScript.dll
assembly:
<asp:ScriptReference Assembly=”Microsoft.Web.Preview” Name=”PreviewScript.js” />
As you can see, Listing 8-31 uses the same script reference because the same JavaScript file also contains
the definition of the Image client control
Listing 8-31: A Page that uses the Image client control
Trang 21function pageLoad() {
var type = Sys.Preview.UI.Image;
var properties = { imageURL: “wroxProgrammerSmall.jpg”, alternateText : “Wrox Programmer’s Reference Series”, width: 155, height: 58 };
var events = null;
var references = null;
var element = $get(“myImage”);
$create(type, properties, events, references, element);
} </script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
The pageLoad method uses the $create shortcut method (the shortcut for the create static method
of the Component base class) to instantiate and initialize an Image client control and to add the control
to the _components collection of the Application object that represents the current ASP.NET AJAX application pageLoad passes the following parameters into the create method:
❑ type : This parameter references the constructor of the component being created, which is the
Sys.Preview.UI.Image constructor in this case
❑ properties : This parameter references an object (normally an object literal) that contains the names and values of the properties of the component being created that you want to initialize The create method internally assigns these values to the properties with the specified names
In this case, the object literal contains four name/value pairs where the name and value parts of each pair respectively contain the name and value of a particular property of the Image client control being created:
var properties = { imageURL: “wroxProgrammerSmall.jpg”, alternateText : “Wrox Programmer’s Reference Series”, width: 155, height: 58 };
❑ events : This parameter references an object (normally an object literal) that specifies the event handlers that you want to register for events with the specified names In this case, the events object is null so any event handlers will be registered
❑ references : This parameter references an object (normally an object literal) that specifies the values of the properties of the component being created that reference other components in the
_components collection of the current Application object In this case, this object is null