Introduction
In my previous article, I have presented the
AJAX Grid and a Generic Pager, which mimics the built-in GridView
control on the client side. In this part, I will provide details about developing Ajax Controls with the
ASP.NET AJAX Framework.
Although the main goal of this article is to show you how to develop ASP.NET AJAX
Controls with the ASP.NET AJAX Framework, it's really worth to read this, even if you
are only interested in ASP.NET AJAX Framework instead of developing controls.
Developing ASP.NET AJAX Control requires both client and server side knowledge of
the ASP.NET AJAX Framework. Certainly, the client side requires more knowledge and
coding compared to the server side. In this part I will cover both; let’s start with the client side.
Client Side
The real beauty of the Microsoft Ajax Library is that it extends JavaScript’s object oriented
model and provides an enhanced type system, which contains namespace, class,
interface, enum, exception, reflection and other constructs
that .NET developers are familiar with. If you are new to this enhanced type system or do not know how to create
namespaces, classes, interfaces in ASP.NET AJAX following the prototype model, I
recommend you to read
Extending JavaScript with ASP.NET AJAX
first before moving forward with this article.
Although the Ajax controls are inherited from Sys.UI.Control, the heart of Ajax controls is the
Sys.Component class. This class mostly resembles the .NET Framework’s
System.ComponentModel.Component class. The following diagram will give you a good understanding of
the inheritance chain related to Ajax control development.
Figure 1: Classes involved in Ajax control development

Sys.IDisposable
This interface is similar to the .NET Framework’s IDisposable interface. The resources that the
component utilizes should be released in the dispose method. These include event handlers, large arrays and so
on.
Sys.INotifyDisposing
This interface allows the consumer of the component to detect its disposing through the disposing
event.
Sys.INotifyPropertyChange
This interface allows the consumer of the component to detect the property changes through the
PropertyChanged event. It will be covered in more depth in the Properties section.
Sys.Component
The Sys.Component class implements all the above interfaces. Component that encapsulate complex
logic or contain child DOM elements require a central place to initialize and cleanup an instance.
This is usually done by overriding the initialize and dispose method of the
Sys.Component class. The following diagram shows the signatures of Sys.Component:
Figure 2: Signatures of the Sys.Component class

There are two kinds
of components:
A nonvisual component does not have any user interface. Examples are the built-in timer control of the
ASP.NET AJAX Framework or a custom component that queues web service calls. Nonvisual
components are similar to the ASP.NET ObjectDataSource, TableAdapter
controls, which don’t have any user interface.
On the other hand, visual components provide a user interface. Examples are the controls of the Ajax Control
Toolkit, the UpdateProgress, the Ajax Grid and so on. Visual components are divided
into:
Behavior or Sys.UI.Behavior
Control or Sys.UI.Control
Sys.UI.Behavior
The purpose of a Behavior is to extend a DOM element without changing its core
functionality. Most of the controls of the Ajax Control Toolkit are behaviors, such as the AutoComplete
TextBox, MaskEdit, DragPanel etc. A behavior must have an associated
DOM element. A single DOM element can have more than one associated behavior. Since
this article is about control development, I’m going to skip behaviors. If you are interested to learn more on
behaviors, I recommend you visit the
Ajax Control Toolkit site.
Sys.UI.Control
On the contrary, a control is itself the DOM element. The main purpose of a
control is to provide new functionality by wrapping an existing control. Examples are
the UpdatePanel, UpdateProgress or the new Tab Control of the Ajax Control
Toolkit. The following listing will show you the minimum code required to create a control.
Listing 1: A Control Skeleton
The above is just a skeleton of a control. As you can see, the associated
DOM element is passed in the control constructor. We are also overriding the
initialize and dispose methods in order to perform the initializing and cleaning up.
Since this is just a dummy control, we are not doing anything except calling the base class methods.
The following diagram shows the signatures of Sys.UI.Control class.
Figure 3: Signatures of the Sys.UI.Control class

Another difference between a control and a Behavior is that a Behavior
allows you to set an id while a control does not allow it. In fact, the id
of a control is the same as the associated DOM element. The following is a quick
reference for the above signatures:
get_element(): Returns the DOM Element that the control represents.
-
get_id(): Returns the id of the control, which is used by the
$find statement to refer it.
-
set_id(): if you try to set id, will you get an Error.invalidOperation
exception, as control does not allow to set the id.
get_parent(): Returns the Parent control.
-
set_parent(): Sets the Parent control.
-
get_visibilityMode(): visibilityMode is an enum which contains the
value hide and collapse.
-
set_visibilityMode(): Sets the visibilityMode. Basically the
visibilityMode works with the control DOM element style's
display property.
-
get_visibile() : Returns whether the control is visible or not based
upon the DOM element style visibility.
-
set_visibile() : Sets the DOM element style visibility to
hidden or visible.
-
addCssClass(): Adds the specified CSS class in DOM
Element className.
-
dispose(): Inherited from Sys.Component.
-
initialize(): Inherited from Sys.Component.
-
onBubbleEvent(): Handles the event which is raised by the
raiseBubbleEvent. if this method returns true the event will not be bubbled to its
parent, it is recommended to return falseif you do not handle the event.
-
raiseBubbleEvent(): Bubbles an event to parent control, basically both
onBubbleEvent and this method is used when you are creating complex controls such
control contains one or more child controls and you want to bubble an event from the child to its
parent control.
-
removeCssClass(): Removes the specified CSS class from the
DOM element className.
-
toggleCssClass(): The specified CSS class will be added in the
classNameif it has not been set previously, if the specified class is already set, then the class is
removed from the className
In the next example, we will create an enhanced ImageButtoncontrol, which will show
a different image on mouse hover.
Listing 2: ImageButton.js
As you can see, we are overriding the initialize method to hook up the
mouseover, mouseout and clickevents of the DOM element, so
that we can set the proper image in those events and raise the clickevent. We are also overriding
the dispose method to detach the event handlers that we have set in the initialize
method.
Properties
Adding Properties to a control is very simple. The recommended way is to declare a
privatevariable in the control constructor and add the getter and
setter in the prototype section. The ASP.NET AJAX Framework always adds
the get_ and set_ prefix when accessing the properties. So when adding the
getter/setter we should follow the get_propertyName and set_propertyName convention. As
done in the above example for the hoverImageUrl property, we are using get_hoverImage()
as the getter and set_hoverImage() as the setter. But when creating the control with a
$create() statement, we are only passing the hoverImageUrlbecause the ASP.NET
AJAX Framework adds the set_prefix. Another important concern with properties is to notify
the client of the control when its property has changed. The following snippet shows how to raise
the propertyChangedevent. Since it only applies to the setter, I am excluding the getter part:
Methods
There is no special consideration except that it is recommended to add the methods in the
prototype object of the control.
Events
Adding Events is similar to adding Properties. First, declare the handler as a
private variable in the control constructor and create a pair of add/remove methods for
that event in the prototype object. Although it is not required to have event handler variable in
module level, but there are certainly some advantages of having it, which I will discuss in the
Function.createDelegate() section of the Ajax Framework. The Ajax
Frameworks always adds the add_ and _removeprefix when adding/removing the events. So,
when adding the event subscription code, the method should be named as add_eventNameand
remove_eventName. As in the above example for click event we are adding the event
subscription methods as add_click and remove_click, but in the $create
statement we are naming it as click. The Ajax Framework takes care of adding the
add_ prefix. As you can see in the above example, both add_click and
remove_click refer to this.get_event() (inherited from Sys.Component). The
Sys.Component class maintains an internal list of subscribed events which is stored in an instance
of Sys.EventHandlerList class together with their respective handlers. Apart from this method pair,
it is recommended to have a raiseEventName method to raise the event. Since the above is a very
basic example, I have excluded it. But you should add it when you have to raise the same event from many places.
When raising an event, we can pass some contextual data called the event arguments. If you recall my previous
article, the Ajax Grid control needs to pass the data item and html table row in the
rowDataBound event. The recommended way to pass the argument(s) with the event is to create a new
class which inherits from the Sys.EventArgs and that takes required argument(s) in the constructor.
For each argument, it will expose the getter and setter in the prototype object. The following code
shows how the Ajax Grid Control rowDataBound of my previous article works.
Currently there are two built-in classes for the event arguments in the Microsoft Ajax
Library, Sys.EventArgs and Sys.CancelEventArgs. Even if you have an event that does not
need any event arguments, you should pass Sys.EventArgs.Empty - like the
ImageButtoncontrol in the above example - so that the subscriber can follow the same pattern to
handle all the events. For example, the Ajax Grid Control and Pager’s events (in my previous
article) are handled in following way:
As you can see, both the Ajax Grid and the Pager follow the same pattern -
HandleEvent(sender, e) - to handle all the events. This is also the same pattern
implemented by the .NET framework to handle events. The Ajax Framework Team did a wonderful job to
make the whole Ajax framework similar to the .NET framework.
Few Important Ajax Framework Methods
The followings are some of the important methods which you need to know in order to develop client
controls.
$get()
It is an alias – or shortcut - for document.getElementById. It takes an optional parent
DOM element. If the parent is not specified, the search starts from the document
object.
$create()
This is a shortcut for Sys.Component.create, used to instantiate a client Component,
Behavior or control.
The complete signature of this method is:
Example:
The first argument takes the full type name of
the object. For example, when creating the ImageButtonControl, we are passing
Simple.Controls.ImageButton. This argument is mandatory.
The second argument takes an array of key/value pair for properties. For example, when creating the
ImageButtoncontrol, we are passing the hoverImageUrland its value as
{'hoverImageUrl':'Images/update-h.gif'}. If we want to set more properties, we will add a comma
separated pair. For example:
The third argument is
almost the same as the second. This argument is for events, and takes an array of key/value pairs. In the
previous example, we are subscribing to the click event with the onButtonClick
handler.
The fourth argument takes an array of key/value pairs with references to components that are required by the
component that is being created. Usually, the components references are exposed through properties in the
component that is being created. Since the Image Button does not require any reference to external components, we
are simply passing nullas an argument value.
The fifth and the last argument is a DOM Element. It is a mandatory argument for Behaviors and
Controls, but optional for a nonvisual component.
$find()
It is similar to $get, but it returns component or descendents instead of DOM
elements. It expects an optional parent of type IContainer from which to start the search. If parent
is not specified, it starts from Sys._Application.
Function._validateParams
JavaScript is not a strongly typed language. Ensuring that method arguments are of the expected
types requires quite a lot of code, like typeOf, parseInt, parseFloat,
(arguments[0] != null) etc statements. And maybe it’s for this reason that the Ajax Team has
included this powerful function which can be used to validate all the method parameters with a single statement.
Let’s take a look at a quick example:
In the above example, we are ensuring that argument value is a string data type and not null. The signature of
this function is:
The first parameter
is an array of arguments, usually it is the arguments variable. The second parameter is an array of
definition of each argument. Each definition can contain:
name: name of the argument.
-
type: datatype of the argument, such as String, Number, Boolean, Array or any other
custom type that you have created.
-
mayBeNull: trueor false, the default is
false.
-
optional: trueor false, the default is
false.
-
parameterArray: trueor false, the default is
false.
-
integer: trueor false, the default is false.
-
elementType: If the argument is array then the type that the array holds.
-
elementInteger: If the argument is array then it ensures that the array holds
integers.
-
elementDomElement: If the argument is array then it ensures that the array holds
DOM elements.
-
elementMayBeNull: trueor false. If the argument is an
array then it ensures whether the array can hold any null items.
If the validation fails, Function._validateParams returns an exception that should be thrown by
the caller of the function. Here is another example from my previous article, where I am ensuring that
columns is an array and each item it holds is an Ajax.Controls.GridColumn instance.
Also, the array cannot contain any null items.
$addHandler
It is a shortcut for Sys.UI.DomEvent.addHandler. It provides a consistent way to hook up
DOM elements’ events and hides the complexity of hooking up events in different browsers. The method
signature is the following:
The first argument is a
reference to the DOM element. The second one is the name of the event without the 'on' prefix, which
is automatically added by the Microsoft Ajax. The third and last argument is the event handler. For example, in
the following code I am binding the click event of a button:
$removeHandler
Removes an event handler that was previously added with $addHandler. The signature of this method
is the same as $addHandler. It is a good practice to remove all the event handlers in the
pageUnload event.
$addHandlers
This is similar to $addHandler, but it allows subscribing to multiple events of the same
DOM element with a single statement. For example
In the above code, we are hooking up the mouseover, mouseout and
click events with a single statement. The first parameter is the reference to the DOM
element; the second is an array of event name/handler pairs. The last parameter is an optional parameter which
usually points to this.
$clearHandlers
Removes all the event handlers which were previously added by $addHandler or
$addHandlers. It takes a single parameter with a reference to a DOM element. For
example, the following statement clears all the event handlers added to a DOM element:
Function.createDelegate
This is a special function which wraps an existing function and returns a new function. The main purpose of
this function is to resolve the this keyword. In the handler for an event raised by a
DOM element, the thiskeyword always refers to the DOM element instead of
the class. Consider the following example; if we code the Image Button control like so, it will
always raise an exception:
Here comes the power of Function.createDelegate. Let’s rewrite the previous code as
follows:
The Function.createDelegatemethod requires two parameters. The first one is the object that will be
pointed by this inside the event handler. The second parameter is the function that will be executed as the event
handler.
Function.createCallback
This is also a special function which wraps an existing function and returns a new function. But the purpose
of this function is to provide contextual data for the callback that is being created. Let’s
consider the Pagercontrol of my previous article, where I have to pass the page number
when the link is created.
The Function.createCallback method requires two parameters. The first one is the
function that will be executed and second one is the context data. In the previous code, we are passing the
current instance and the page number as arguments. Unlike Function.createDelegete, it does not
resolve the this keyword and that’s why we are passing the current instance along with the page
number as a JSON object in the context data.
I also recommend you to check the
Base type extension
Sys.UI.DomElement,
Sys.UI.DomEvent,
Sys.UI.MouseButton and
Sys.UI.Key in the ASP.NET AJAX Documentation.
Server Side
So far, we have discussed the Client Part of Developing Ajax Controls. Lets now discuss the Server Side. Apart
from the usual tasks, it is also responsible for registering the required JavaScript files in the
ScriptManager and injecting the $create statements automatically. So far in this
article we have manually added the $create statements. Let’s see how we can perform these things
automatically. But before that, let’s explore the Server side classes involved in Ajax Controls development.
Figure 4: Server Side class hierarchy

The above diagram shows the inheritance chain for Component, control and
Extender. As you can see, to develop a control (Since Components and Extenders are
beyond the scope of this article, I am skipping them) there are two options: creating a class that inherits from
ScriptControl or creating a class that implements the IScriptControl interface. If you
want to start writing your control from WebControl then certainly
ScriptControl is a better option as it is derived from WebControl. But if you want to
start your control from scratch and do not want the built-in features of WebControl,
then implementing the IScriptControl is the right option. The IScriptControl is also
the right option when you want to add Ajax Framework features in an existing control such as the
ImageButtonControl in this article. But both these approaches require to override the following
methods:
GetScriptDescriptors
GetScriptReferences
GetScriptDescriptors
This is the method responsible for generating the $create statements on the client side. It uses
a special class, ScriptDescriptor, to generate it. Before moving forward, let’s take a quick look at
the ScriptDescriptor class hierarchy:
Figure 5: ScriptDescriptor class hierarchy

I hope you have already
guessed that there are separate descriptors for each type of Component. If you are developing a
regular Component then this method should return ScriptComponentDescriptor instances.
In case of an Extender, the method should return ScriptBehaviorDescriptor instances and
for a control; instances of the ScriptControlDescriptor class. The descriptor has some
special methods which glue the server side with the client side. Let’s see a short example of how a
$create statement is injected from the server side code:
As you can see, we are passing the Client Side type (Although both Client and Server has the same type name)
and the DOM element ID in the constructor. This fills the first and last argument of
the $create statement. After that, we are setting the hoverImageUrl property that fills
the property section of the $create() statement. Finally, we are filling the Event part
by setting the click event handler. Here are the few important methods exposed by script
descriptors:
AddProperty()
This adds a Property in the client side. The First argument is the name of the property and the second one is
the value.
AddEvent()
This adds an event in the client side, the first parameter is the event name and second one is the name of the
function that you want to bind.
AddScriptProperty()
This method assigns JavaScript code as a value for the given property. It is usually required for
complex property assignment, such as the Columns collection of the Ajax Grid of my previous article.
AddElementProperty()
This method adds a property on the client side, but the difference with the AddProperty is that
the value is passed as an argument to the $get method.
AddComponentProperty()
It adds a component reference in the component section of $create statement. The value will be
used with the $find statement for assigning the property. The first parameter is the name of the
property and the second one is the id of the component.
GetScriptReferences
This method is responsible for registering the JavaScript files in ScriptManager. In
this method we will need to create an instance of ScriptReference for each required
JavaScript file. For example, in the Image Button we are registering the JavaScript
file as follows:
We can also register the
JavaScript files as embedded resources. To do this, we first have to mark a JavaScript
file as an embedded resource in the Solution Explorer in Visual Studio. Then, we have to add the
WebResource attribute to utilize the Asp.net 2.0 Web Resource Handler. The following code shows how
to register a JavaScript file as an embedded resource:
We also have to override the OnPreRender and Render methods if we are implementing
the IScriptControl instead of inheriting from ScriptControl. This will make sure that
the ScriptManger recognizes the server control as an Ajax enabled control. The
following listing shows the minimum code required on the server side:
As you can see we are registering the control in the OnPreRender method;
and script descriptors in the Render method. We are also throwing an error if there is no
ScriptManager in the page. But you can certainly exclude that if you are planning to develop
controls that work differently based upon the presence of the ASP.NET AJAX
Framework.
Summary
I hope to have covered enough to get you started in Developing Ajax Controls with ASP.NET
AJAX. If you have recently checked the ASP.NET Future package, Silverlight and Astoria, you already know that the Ajax framework has a long way to go
and certainly control development will play a big role in those environments.
About Kazi Manzur Rashid
 |
Kazi Manzur Rashid, Nickname Amit is a Diehard fan of Microsoft Technology. He started programming with Visual Basic 5.0 back in 1996. Since then he has developed many diversified solutions in his professional career, which spans from Anti-Spyware Tool to Personalized Start Page. Currently He is wor...
This author has published 10 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
New article: How to detect and avoid memory and resources leaks in .NET applications
read more
MSDN Guidance on ASP.NET MVC vs WebForms and its Impact on my EF + ASP.NET Work
read more
VS 2010 and .NET 4.0 Beta 2
read more
A new approach to build iGoogle/Facebook like sites with Asp.Net: Kalitte Widget Toolkit
read more
Important releases from Microsoft you may have missed
read more
Designer v Xaml v Code
read more
Choose your preferred data layout with RadListView for ASP.NET AJAX
read more
ClientIDMode in ASP.NET 4.0
read more
DevReach Follow-up, Part I
read more
Let us know what you want!
read more
|
|
Please login to rate or to leave a comment.