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

ASP.NET AJAX Programmer’s Reference - Chapter 8 potx

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 (903.44 KB, 42 trang )

Developing 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.
Control
This section discusses the methods and properties of the Control base class. Because your custom
controls must override the members of the
Control class, you need to have a good understanding
of what each member does and how you should override them to provide your own implementa-
tion for these members.
Definition
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.
c08.indd 281c08.indd 281 8/20/07 6:05:04 PM8/20/07 6:05:04 PM
Chapter 8: Developing Client Controls
282


Listing 8-1: The Definition of the Control Class
Sys.UI.Control = function Sys$UI$Control(element) {
if (typeof(element.control) != ‘undefined’)
throw Error.invalidOperation(Sys.Res.controlAlreadyDefined);

Sys.UI.Control.initializeBase(this);
this._element = element;
element.control = this;
this._oldDisplayMode = this._element.style.display;
if (!this._oldDisplayMode || (this._oldDisplayMode == ‘none’))
this._oldDisplayMode=’’;
}

Sys.UI.Control.prototype={
_ parent: null,
_visibilityMode: Sys.UI.VisibilityMode.hide,
get_element: Sys$UI$Control$get_element,
get_id: Sys$UI$Control$get_id,
set_id: Sys$UI$Control$set_id,
get_parent: Sys$UI$Control$get_parent,
set_parent: Sys$UI$Control$set_parent,
get_visibilityMode: Sys$UI$Control$get_visibilityMode,
set_visibilityMode: Sys$UI$Control$set_visibilityMode,
get_visible: Sys$UI$Control$get_visible,
set_visible: Sys$UI$Control$set_visible,
addCssClass: Sys$UI$Control$addCssClass,
dispose: Sys$UI$Control$dispose,
initialize: Sys$UI$Control$initialize,
onBubbleEvent: Sys$UI$Control$onBubbleEvent,
raiseBubbleEvent: Sys$UI$Control$raiseBubbleEvent,

removeCssClass: Sys$UI$Control$removeCssClass,
toggleCssClass: Sys$UI$Control$toggleCssClass
}

Sys.UI.Control.registerClass(‘Sys.UI.Control’, Sys.Component);
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;
c08.indd 282c08.indd 282 8/20/07 6:05:05 PM8/20/07 6:05:05 PM
Chapter 8: Developing Client Controls
283
As 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 :
this._oldDisplayMode = this._element.style.display;
if (!this._oldDisplayMode || (this._oldDisplayMode == ‘none’))
this._oldDisplayMode=’’;
get_element
The get_element method of the Control class returns a reference to the DOM element that the

Control represents, as shown in Listing 8-2 .
Listing 8-2: The get_element Method of the Control Class
function Sys$UI$Control$get_element() {
return this._element;
}
get_id
As discussed in the previous chapter, the Component base class exposes a property named id whose
value uniquely identifies a component among other components stored in the
Application object’s

_components collection. Because the Control class derives from the Component base class, every

Control object is also a Component object and consequently is added to the _components collection of
the
Application object. This means that every Control object must have a unique id value.
Because a
Control object is an ASP.NET AJAX representation of a DOM element in an ASP.NET AJAX
application, it makes lot of sense to use the value of the DOM element’s

id HTML attribute as the id of
the
Control object that represents the DOM element. Therefore, the Control class overrides the get_id
method that it inherits from its base class (the
Component class) to return the value of the id attribute of
the DOM element that the
Control represents, as shown in Listing 8-3 .
c08.indd 283c08.indd 283 8/20/07 6:05:05 PM8/20/07 6:05:05 PM
Chapter 8: Developing Client Controls
284
Listing 8-3: The get_id Method of the Control Class
function Sys$UI$Control$get_id() {
if (!this._element)
return ‘’;
return this._element.id;
}
set_id
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;
while (current) {
if (Array.contains(parents, current))
throw Error.invalidOperation(Sys.Res.circularParentChain);

parents[parents.length] = current;
current = current.get_parent();
}
this._parent = value;
}
c08.indd 284c08.indd 284 8/20/07 6:05:06 PM8/20/07 6:05:06 PM
Chapter 8: Developing Client Controls
285
get_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.
c08.indd 285c08.indd 285 8/20/07 6:05:06 PM8/20/07 6:05:06 PM
Chapter 8: Developing Client Controls
286
get_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() {
throw Error.notImplemented();
}


Sys.UI.VisibilityMode.prototype = {
hide: 0,
collapse: 1
}

Sys.UI.VisibilityMode.registerEnum(“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
represents.
Listing 8-9: The get_visible Method of the Control Class
function Sys$UI$Control$get_visible() {
return (this._element.style.visibility != ‘hidden’);
}
set_visibilityMode
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
c08.indd 286c08.indd 286 8/20/07 6:05:06 PM8/20/07 6:05:06 PM
Chapter 8: Developing Client Controls
287
Control 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;
}
set_visible
Listing 8-11 presents the internal implementation of the set_visible method of the Control class. As
you can see, the
visible property of a Control object basically reflects the visibility property of the

style property of the DOM element that the Control object represents. In other words, the visible
property of a
Control object allows you to treat the visibility of the underlying DOM element as a Bool-
ean value as opposed to a string value such as
visible or hidden .
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’;
}
}
c08.indd 287c08.indd 287 8/20/07 6:05:07 PM8/20/07 6:05:07 PM
Chapter 8: Developing Client Controls
288

add 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,
as shown in Listing 8-12 .

Listing 8-12: The add CssClass Method of the Control Class
function Sys$UI$Control$addCssClass(className) {
Sys.UI.DomElement.addCssClass(this._element, className);
}
remove CssClass
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.
c08.indd 288c08.indd 288 8/20/07 6:05:07 PM8/20/07 6:05:07 PM
Chapter 8: Developing Client Controls
289
Listing 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 implemen-
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
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.
c08.indd 289c08.indd 289 8/20/07 6:05:07 PM8/20/07 6:05:07 PM
Chapter 8: Developing Client Controls

290
raise 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();
while (currentTarget) {
if (currentTarget.onBubbleEvent(source, args))
return;

currentTarget = currentTarget.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.
c08.indd 290c08.indd 290 8/20/07 6:05:08 PM8/20/07 6:05:08 PM
Chapter 8: Developing Client Controls
291
Every 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:
Type.registerNamespace(‘Sys.Preview’);
The Sys.Preview namespace contains a UI namespace defined as follows:

Type.registerNamespace(‘Sys.Preview.UI’);
The Sys.Preview.UI namespace contains several client controls that directly or indirectly derive from
the ASP.NET AJAX
Control base class. The following sections walk you through the code for these cli-
ent controls to help you gain the skills you need to develop your own custom client controls. You’ll also
take a look at the code for Web pages that use these client controls.
Label Client Control
The ASP.NET AJAX Label client control is the ASP.NET AJAX representation of the <span> HTML ele-
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 con-
trol, as shown in Listing 8-19 .

c08.indd 291c08.indd 291 8/20/07 6:05:08 PM8/20/07 6:05:08 PM
Chapter 8: Developing Client Controls
292
Listing 8-19: The Getter and Setter Methods of the html Encode Property
function Sys$Preview$UI$Label$get_htmlEncode()
{
return this._htmlEncode;
}

function Sys$Preview$UI$Label$set_htmlEncode(value)
{
this._htmlEncode = value;
}
text
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
function Sys$Preview$UI$Label$get_text()
{
var element = this.get_element();

if (this._htmlEncode)
return element.innerText;
else

return element.innerHTML;
}
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:
if (this._htmlEncode)
return element.innerText;
If not, it returns the value of the innerHTML property of the DOM element that the Label control
represents:
else
return element.innerHTML;
Listing 8-21 presents the implementation of the set_text method of the Label control.
c08.indd 292c08.indd 292 8/20/07 6:05:08 PM8/20/07 6:05:08 PM
Chapter 8: Developing Client Controls
293
Listing 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.
c08.indd 293c08.indd 293 8/20/07 6:05:09 PM8/20/07 6:05:09 PM
Chapter 8: Developing Client Controls
294
prototype
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
Sys.Preview.UI.Label.prototype =
{
_htmlEncode: false,

get_htmlEncode: Sys$Preview$UI$Label$get_htmlEncode,
set_htmlEncode: Sys$Preview$UI$Label$set_htmlEncode,
get_text: Sys$Preview$UI$Label$get_text,
set_text: Sys$Preview$UI$Label$set_text
}
descriptor
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 } ]
}
c08.indd 294c08.indd 294 8/20/07 6:05:09 PM8/20/07 6:05:09 PM
Chapter 8: Developing Client Controls
295
Using 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
<%@ 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”>
var label;

function clickcb(domEvent)
{
var chkbx = $get(“chkbx”);
label.set_htmlEncode($get(“chkbx”).checked);
var txtbx = $get(“txtbx”);
label.set_text(txtbx.value);
}

function pageLoad()

{
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”>
<Scripts>
<asp:ScriptReference Assembly=”Microsoft.Web.Preview”
Name=”PreviewScript.js” />
</Scripts>
</asp:ScriptManager>
<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 />
<span id=”myspan”></span>
<div>
</div>
</form>
</body>
</html>
c08.indd 295c08.indd 295 8/20/07 6:05:09 PM8/20/07 6:05:09 PM
Chapter 8: Developing Client Controls
296
Figure 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-2
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.
c08.indd 296c08.indd 296 8/20/07 6:05:10 PM8/20/07 6:05:10 PM
Chapter 8: Developing Client Controls
297
Note 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:
var chkbx = $get(“chkbx”);
It then passes the check box status into the set_htmlEncode method of the Label client control:
label.set_htmlEncode($get(“chkbx”).checked);
Finally, it calls the set_text method on the Label client control that represents the <span> element to
display the value entered into the text box:
var txtbx = $get(“txtbx”);
label.set_text(txtbx.value);
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 frame-
work. It is the ASP.NET AJAX representation of an image DOM element. As such, it exposes the DOM

width , height , src , and alt properties of this DOM element as the width , height , imageURL , and
c08.indd 297c08.indd 297 8/20/07 6:05:10 PM8/20/07 6:05:10 PM
Chapter 8: Developing Client Controls
298
alterateText properties on the Image client control itself. This allows you to treat these DOM proper-
ties as properties 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
Sys.Preview.UI.Image =
function Sys$Preview$UI$Image(associatedElement)
{
Sys.Preview.UI.Image.initializeBase(this, [associatedElement]);

}
Sys.Preview.UI.Image.registerClass(‘Sys.Preview.UI.Image’, Sys.UI.Control);
prototype
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
Sys.Preview.UI.Image.prototype =
{
get_alternateText: Sys$Preview$UI$Image$get_alternateText,
set_alternateText: Sys$Preview$UI$Image$set_alternateText,
get_height: Sys$Preview$UI$Image$get_height,
set_height: Sys$Preview$UI$Image$set_height,
get_imageURL: Sys$Preview$UI$Image$get_imageURL,
set_imageURL: Sys$Preview$UI$Image$set_imageURL,
get_width: Sys$Preview$UI$Image$get_width,
set_width: Sys$Preview$UI$Image$set_width
}
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
counterpart to make client-side programming feel more like server-side ASP.NET programming.
c08.indd 298c08.indd 298 8/20/07 6:05:10 PM8/20/07 6:05:10 PM
Chapter 8: Developing Client Controls
299
image URl
The Image client control exposes two methods named get_imageURL and set_imageURL that allow
you to get and set the value of the
src property of the underling DOM element, as shown in Listing 8-27 .
As you can see from this code listing, both methods first call the
get_element method to return a
reference to the
<img> element that the Image client control represents. The Image client control inherits
this method from its
Control base class.
Listing 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)
{
this.get_element().src = value;
}
width

The Image client control exposes two methods named get_width and set_width that allow you to get
and set the value of the
width property of the image DOM element that the control represents. As you
can see in Listing 8-28 , these methods are just wrappers around the
width property of the DOM element,
which means you can treat this the same way as a property on an ASP.NET AJAX object.

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)
c08.indd 299c08.indd 299 8/20/07 6:05:11 PM8/20/07 6:05:11 PM
Chapter 8: Developing Client Controls

300
Listing 8-29 (continued)
}
function Sys$Preview$UI$Image$set_height(value)
{
this.get_element().height = value;
}
alternate Text
The 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
Control
function Sys$Preview$UI$Image$get_alternateText()
{
return this.get_element().alt;
}

function Sys$Preview$UI$Image$set_alternateText(value)
{
this.get_element().alt = value;
}
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
<%@ 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”>

c08.indd 300c08.indd 300 8/20/07 6:05:11 PM8/20/07 6:05:11 PM
Chapter 8: Developing Client Controls
301
function 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”>
<Scripts>
<asp:ScriptReference Assembly=”Microsoft.Web.Preview”
Name=”PreviewScript.js” />
</Scripts>
</asp:ScriptManager>
<img id=”myImage” />
</form>
</body>
</html>
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
c08.indd 301c08.indd 301 8/20/07 6:05:11 PM8/20/07 6:05:11 PM
Chapter 8: Developing Client Controls
302
because the Image client control does not expose any properties that reference other compo-
nents of the application.
❑ element : This parameter references the DOM element on the current page that the newly cre-
ated
Image client control will represent. In this case, this parameter references the <img> HTML
element with
id HTML attribute value of “ myimage“ :
var element = $get(“myImage”);
T h e pageLoad method then invokes the create method, passing in the five parameters to create the

Image client control:
$create(type, properties, events, references, element);
Extending Image Client Control
In this section, we’ll implement a client control named Image2 that extends the functionality of the

Image client control to add support for a DHTML feature known as transition . The Image2 client control

will use the transition feature to provide an animated effect when the user moves the mouse pointer over
and out of the DOM
image element that the control represents. Because this feature is only supported on
Internet Explorer version 4 (IE4) or higher, we need a way to ensure that the
Image2 client control is
used in only IE4 or higher.
The ASP.NET AJAX client-side framework defines an object named
browser , as presented in Listing 8-32 ,
which emulates the
HttpBrowserCapabilities class in the ASP.NET Framework.
Listing 8-32: The browser Object
Sys.Browser = {};
Sys.Browser.InternetExplorer = {};
Sys.Browser.Firefox = {};
Sys.Browser.Safari = {};
Sys.Browser.Opera = {};
Sys.Browser.agent = null;
Sys.Browser.hasDebuggerStatement = false;
Sys.Browser.name = navigator.appName;
Sys.Browser.version = parseFloat(navigator.appVersion);
if (navigator.userAgent.indexOf(‘ MSIE ‘) > -1)
{
Sys.Browser.agent = Sys.Browser.InternetExplorer;
Sys.Browser.version =
parseFloat(navigator.userAgent.match(/MSIE (\d+\.\d+)/)[1]);
Sys.Browser.hasDebuggerStatement = true;
}

else if (navigator.userAgent.indexOf(‘ Firefox/’) > -1)
{

Sys.Browser.agent = Sys.Browser.Firefox;
Sys.Browser.version =
parseFloat(navigator.userAgent.match(/ Firefox\/(\d+\.\d+)/)[1]);

c08.indd 302c08.indd 302 8/20/07 6:05:12 PM8/20/07 6:05:12 PM
Chapter 8: Developing Client Controls
303
Sys.Browser.name = ’Firefox’;
Sys.Browser.hasDebuggerStatement = true;
}

else if (navigator.userAgent.indexOf(‘ Safari/’) > -1)
{
Sys.Browser.agent = Sys.Browser.Safari;
Sys.Browser.version =
parseFloat(navigator.userAgent.match(/ Safari\/(\d+\.\d+)/)[1]);
Sys.Browser.name = ’Safari’;
}

else if (navigator.userAgent.indexOf(‘Opera/’) > -1)
{
Sys.Browser.agent = Sys.Browser.Opera;
}
As you can see in this listing, the ASP.NET AJAX client-side framework automatically populates the

browser object with the information about the current browser when the ASP.NET AJAX scripts are
downloaded. The
browser object exposes two important properties named agent and version that
specify the type and version of the current browser. The possible values of the
agent property are

Sys.Browser.InternetExplorer , Sys.Browser.Firefox , Sys.Browser.Safari , and Sys.Browser
.Opera
.
Now back to the original goal, which is implementing the
Image2 client control that supports the

transition DHTML feature. Listing 8-33 presents the Image2.js JavaScript file that defines the Image2
client control. The following sections discuss all the members of this control.

Listing 8-33: The Content of the Image2.js File
Type.registerNamespace(“CustomComponents”);
CustomComponents.Image2 =
function CustomComponents$Image2(associatedElement)
{
if (Sys.Browser.agent != Sys.Browser.InternetExplorer ||
Sys.Browser.version < 4)
throw Error.invalidOperation;

CustomComponents.Image2.initializeBase(this, [associatedElement]);
associatedElement.style.filter = ”revealTrans(duration=0.4, transition=1)”;
}

function CustomComponents$Image2$set_imageURL(value)
{
this.mouseOutImageURL = value;
CustomComponents.Image2.callBaseMethod(this, “set_imageURL”, [value]);
}

function CustomComponents$Image2$get_mouseOverImageURL()
{

return this.mouseOverImageURL;
}

(continued)
c08.indd 303c08.indd 303 8/20/07 6:05:12 PM8/20/07 6:05:12 PM
Chapter 8: Developing Client Controls
304
Listing 8-33 (continued)
function CustomComponents$Image2$set_mouseOverImageURL(value)
{
this.mouseOverImageURL = value;
}

function CustomComponents$Image2$mouseOverCallback ()
{
this.get_element().filters[“revealTrans”].apply();
this.get_element().src = this.mouseOverImageURL;
this.get_element().filters[“revealTrans”].play();
}

function CustomComponents$Image2$mouseOutCallback ()
{
this.get_element().filters[“revealTrans”].apply();
this.get_element().src = this.mouseOutImageURL;
this.get_element().filters[“revealTrans”].play();
}

function CustomComponents$Image2$get_duration()
{
return this.get_element().filters[“revealTrans”].duration;

}

function CustomComponents$Image2$set_duration(value)
{
this.get_element().filters[“revealTrans”].duration = value;
this.get_element().filters[“revealTrans”].apply();
}

function CustomComponents$Image2$get_transition()
{
return this.get_element().filters[“revealTrans”].transition;
}

function CustomComponents$Image2$set_transition(value)
{
this.get_element().filters[“revealTrans”].transition = value;
this.get_element().filters[“revealTrans”].apply();
}

function CustomComponents$Image2$initialize()
{
CustomComponents.Image2.callBaseMethod(this, “initialize”);
this.mouseOverDelegate = Function.createDelegate(this,
this.mouseOverCallback);
this.mouseOutDelegate = Function.createDelegate(this,
this.mouseOutCallback);
$addHandler(this.get_element(), “mouseover”, this.mouseOverDelegate);
$addHandler(this.get_element(), “mouseout”, this.mouseOutDelegate);
}


c08.indd 304c08.indd 304 8/20/07 6:05:12 PM8/20/07 6:05:12 PM
Chapter 8: Developing Client Controls
305
function CustomComponents$Image2$dispose()
{
$removeHandler(this.get_element(), “mouseover”, this.mouseoverDelegate);
$removeHandler(this.get_element(), “mouseout”, this.mouseOutDelegate);
CustomComponents.Image2.callBaseMethod(this, “dispose”);
}

CustomComponents.Image2.prototype =
{
set_imageURL: CustomComponents$Image2$set_imageURL,
get_mouseOverImageURL : CustomComponents$Image2$get_mouseOverImageURL,
set_mouseOverImageURL : CustomComponents$Image2$set_mouseOverImageURL,
get_duration : CustomComponents$Image2$get_duration,
set_duration : CustomComponents$Image2$set_duration,
get_transition : CustomComponents$Image2$get_transition,
set_transition : CustomComponents$Image2$set_transition,
mouseOverCallback : CustomComponents$Image2$mouseOverCallback,
mouseOutCallback : CustomComponents$Image2$mouseOutCallback,
initialize : CustomComponents$Image2$initialize
}

CustomComponents.Image2.registerClass(‘CustomComponents.Image2’,
Sys.Preview.UI.Image);

CustomComponents.Transition = function CustomComponents$Transition()
{
throw Error.notImplemented();

}

CustomComponents.Transition.prototype =
{
boxIn : 0,
boxOut : 1,
circleIn : 2,
circleOut : 3,
wipeUp : 4,
wipeDown : 5,
wipeRight : 6,
wipeLeft : 7,
verticalBlinds : 8,
horizontalBlinds : 9,
checkerboardAcross : 10,
checkerboardDown : 11,
randomDissolve : 12,
splitVerticalIn : 13,
splitVerticalOut : 14,
splitHorizontalIn : 15,
splitHorizontalOut : 16,
(continued)
c08.indd 305c08.indd 305 8/20/07 6:05:13 PM8/20/07 6:05:13 PM

×