Published: 26 Jun 2007
By: Kazi Manzur Rashid
Download Sample Code

An in depth guide to developing controls with the ASP.NET AJAX

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:

  • Nonvisual
  • Visual

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.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

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.

Other articles in this category


Animating a Web Form using ASP.NET AJAX
In this article you will learn how to animate a webform using asp.net ajax.
Jquery Ajax
JQuery makes communicating with the server very, very easy. JQuery uses get(), getJSON(), and post(...
jQuery Deferred Objects Promise Callbacks
jQuery Deferred/Promise objects untangle spaghetti-like asynchronous code.
jQuery in Action 2nd edition: Queuing functions for execution
This article is taken from the book jQuery in Action, second edition. This segment shows how you can...
Test120Jan
This is my custom article

You might also be interested in the following related blog posts


AJAX Extender Control: Interaction with List of Controls in Pop-up Window read more
MSDN Guidance on ASP.NET MVC vs WebForms and its Impact on my EF + ASP.NET Work read more
Designer v Xaml v Code read more
November's Toolbox Column Now Online read more
Visual Studio Extensions for RadControls for Silverlight Coming with Q3 2009 Release read more
ClientIDMode in ASP.NET 4.0 read more
Choose your preferred data layout with RadListView for ASP.NET AJAX read more
RadControls for Silverlight 3 Q3 2009 bringing more richness and interactivity to your applications read more
Important releases from Microsoft you may have missed read more
DevReach Follow-up, Part I read more
Top
 
 
 

Discussion


Subject Author Date
placeholder great article Vorn Mom 6/13/2009 12:53
RE: great article Sonu Kapoor 6/15/2009 8:00 AM
placeholder Congratulations Simone Busoli 6/30/2007 1:31 AM
Re: Congratulations Kazi Manzur Rashid 6/30/2007 5:33 AM
placeholder Vote for this article Kazi Manzur Rashid 7/3/2007 6:07 AM
RE: Vote for this article Sergey Milton 4/15/2010 6:50 AM
placeholder Great Article! Mehfuz Hossain 7/5/2007 1:06
Re: Great Article! Kazi Manzur Rashid 7/5/2007 2:42
placeholder Check out my new article Kazi Manzur Rashid 7/9/2007 6:18
my hornour to read this article! Davi Lee 7/11/2007 8:13 AM
placeholder Re: my hornour to read this article! Kazi Manzur Rashid 7/11/2007 8:25 AM
what i think after reading this article Davi Lee 7/13/2007 11:46
placeholder But series of tutorial/book is possible Awnik Raihan 7/15/2007 5:58 AM
Feedback Kazi Manzur Rashid 7/15/2007 8:36 AM
placeholder Great article! Buddhi Pathirane 8/2/2007 10:06 AM
Re: Great article! Kazi Manzur Rashid 8/2/2007 11:14 AM
placeholder Great Pushkar Rathod 12/14/2007 4:12
System.ComponentModel.Component not .Comoponent Rick Anderson 1/15/2008 9:12
placeholder RE: System.ComponentModel.Component not .Comoponent Sonu Kapoor 1/15/2008 9:17
feedback Fakhre Alam 2/9/2009 7:09 AM

Please login to rate or to leave a comment.