You are on page 1of 7

Magic AJAX: Applying AJAX to your existing

Web Pages

Introduction

After all the fuss and the hype about AJAX technologies, now there are plenty of
solutions for AJAX that developers can choose from. The drawback is that you have
to replace the standard ASP.NET controls that your web pages contain with custom
AJAX-enabled controls, and/or write JavaScript code for the client to process the
data returned by the server.
What if there was a "magical" panel control that, when you drag-n-drop ordinary,
old-fashioned, ASP.NET controls, "magically" transforms them to AJAX-enabled
controls? Impossible! Well, this article claims that such a panel can exist
(AjaxPanel) and sadly, there's no magic about it; it's all plain C# code.

Update - 12 Nov 2005: The MagicAjax framework is now hosted on SourceForge.


Many improvements and features have been added since the initial release, including
support for ASP.NET 2.0. Visit MagicAjax's homepage to read about latest news and
to download the latest version.

Background

This article assumes that you know what AJAX is. If that's not the case, there are
plenty of good articles in CodeProject to get you started. The code behind this article
is based on the excellent series AJAX WAS Here by Bill Pierce. Understanding the
client JavaScript framework and how it invokes server methods is not required to use
the classes in this article, but if you want to see what happens "under the hood", I
strongly recommend reading Bill Pierce's articles.

Using the code

I have included a demo project to show you how to convert the existing plain
postback controls to AJAX-like ones.

BubisChat.aspx

This page tries to be a chat application. I will not get into the details of how it works;
it was created just for demonstration purposes. It uses the standard ASP.NET
controls, no mystery here. The buttons cause a postback to the server and the page
reloads and fills the controls with the new data.

AjaxBubisChat.aspx

This page uses the same controls but it works quite differently. The controls are
refreshed instantly and without any reloading by the browser. That's right, AJAX is
here.

Hey, how did you do that

There are four steps required to convert BubisChat.aspx to AjaxBubisChat.aspx (or


to apply AJAX to any page using this framework):

• Make the page inherit from AjaxPage (optional)

public class AjaxBubisChat : Ajax.AjaxPage

Inheriting from AjaxPage is not required; it just handles the callback event
and provides some useful properties for convenience. To handle the callback
event in a page that inherits from AjaxPage, you override the OnCallBack
method:
protected override void OnCallBack(EventArgs e)
{
// Refreshes the sessionID in the cache
PutSessionIDInCache();

txtMsg.Text = chatData.msgText.ToString();
ShowNames();

base.OnCallBack (e);
}

If the page doesn't inherit from AjaxPage, it can handle the callback event by
implementing the ICallBackEventHandler interface:

public interface ICallBackEventHandler


{
void RaiseCallBackEvent();
}

The callback is like a postback but without the reloading of the page by the
browser. I'll explain what the callback does a little later. For reasons that will
become clear later, the Load event of the page and its controls are not raised
during a callback. You must use the callback event instead.

During a callback, the HttpContext of the page is invalid, so you can't use
the Request/Response properties of System.Web.UI.Page, you have to use
the CallBackHelper.Request and CallBackHelper.Response properties
instead. The AjaxPage provides valid Request/Response properties so that
you don't have to replace them in your code for the ones from
CallBackHelper.

• Put inside an AjaxPanel the controls that will get refreshed without a
postback

It can be one AjaxPanel for each TextBox and ListBox, or one AjaxPanel
for all of them. The buttons should be inside an AjaxPanel, so that their
submit function is replaced automatically for a callback function. In this
example, I just put all the controls inside one AjaxPanel for convenience.

• Configure the web.config file

Put:

<httpModules>
<add name="AjaxHttpModule" type="Ajax.AjaxHttpModule, Ajax" />
</httpModules>

at the <system.web> section, and:

<!-- If CallBackScriptPath is not set in the appSettings,


"/ajax/script" is used-->
<add key="CallBackScriptPath" value="/ajax/script" />
at the <appSettings> section of the web.config file.

The AjaxHttpModule processes the callback at the AcquireRequestState


event of the HttpApplication, after the request has been authenticated. The
default script path is valid if you extract the source files to the wwwroot path.
If you extract them to another directory, you should change the
CallBackScriptPath of the application settings accordingly.

• Enable the CallBackTimer on the page (optional)


• // For automatic CallBack every 3 seconds.
CallBackHelper.SetCallBackTimerInterval(3000);

This is required so that the chat textbox is automatically refreshed if there are
new messages to display. Most pages don't need automatic refresh, so just
ignore this if that's the case.

And believe it or not, that was all! No JavaScript and no replacement of the controls
were necessary. You can add new controls to the AjaxPanel either by code or by
using the Visual Studio Designer.

So, what does a callback do

The job of the callback is to invoke server-side control events (and a special
CallBackTimer event if it is enabled). For more details, I suggest reading Bill
Pierce's articles that I have mentioned in the Background section. When the server
receives a callback, it returns generic JavaScript code. The client doesn't care what
the JavaScript does (populating a ListBox, invoking an alert box, whatever), it just
executes them. Thus, contrary to the usual mentality of AJAX applications, it's up to
the server to manipulate the page using JavaScript, not the client. That way, instead
of trying to embed JavaScript code in the web page and synchronize it with the
server code, the focus is shifted on implementing all the functionality of a custom
control on the server side without having to separate the code to the JavaScript part,
and to the C# (VB.NET, whatever) part.

Now, it is getting more interesting... A page that is AJAX-enabled is stored as a


session state variable. When a callback is invoked, the AjaxHttpModule intercepts
it, finds the originating page from the session and invokes the appropriate events for
the controls of the page without reloading the original page.

Why store the page in the session

• There is no overhead because of constant reloading of the page and its


controls.
• The illusion of AJAX is that the page behaves like a desktop application. In a
desktop application the controls are kept in memory without reloading them
every time a button is pressed, and the controls can be added or removed
dynamically. Well, by persisting the page in the session we don't have to
reload the controls and the controls can be added or removed
dynamically; and if you add them to the AjaxPanel they will actually appear
on the browser too!
In order for a page to be stored in the session, it must contain at least one
AjaxControl control (base class of AjaxPanel). The session key that is used is the
URL of the originating page so that different pages can be distinguished from one
another.

The callback doesn't have to come from a control contained inside an AjaxPanel, it
can be invoked from any control on the page, as long as it is properly configured to
call the appropriate callback function, like this:

Button btnSend = new Button();


btnSend.Attributes.Add ("onclick",
CallBackHelper.GetCallbackEventReference(btnSend) +
" return false;");

The CallBackHelper.GetCallbackEventReference method provides the


AJAXCbo.DoPostCallBack call on the client side, and return false; is added so
that the OnClick event can override the submit function.

The AjaxPanel, by default, automatically configures all the submit buttons that it
contains to call the callback function and replaces the __doPostBack calls of normal
postback with a AJAXCbo.DoPostCallBack call. If you want to manually set the
OnClick event of your controls, set the SetCallBackForSubmitButtons and
SetCallBackForChildPostBack properties of AjaxPanel to false.

The mysterious AjaxPanel

The task of AjaxPanel is to reflect its contents on the browser of the client
each time a callback is invoked. To accomplish this, it scans the controls that it
contains and produces the appropriate JavaScript code for every control that is
added, removed, or altered, ignoring the controls that haven't changed at all. In
order to spot changes, it renders each control and checks if the produced HTML is
different from the one obtained during the previous callback.

In addition, if the AjaxPanel encounters any RenderedByScriptControl controls


(AjaxPanel inherits from this class), it ignores them and lets them take care of their
"reflection" to the browser. Thus, if an AjaxPanel (parent) contains another
AjaxPanel (child), and a control of the child-AjaxPanel is altered, the parent-
AjaxPanel won't send the entire HTML rendering of the child-AjaxPanel, but instead
the child-AjaxPanel will send just the HTML of the altered control. That way, the size
of the JavaScript code that the client gets as a response of a callback, is greatly
reduced.

Limitations

The known limitations are:

• You cannot remove an attribute of the AjaxPanel control. The removal will
not be reflected on the page. Use:

ajaxPanel.Attributes["attrib"] = "";
instead.

• The controls contained inside an AjaxPanel doesn't invoke client-side page


validation by default. You have to "manually" insert the appropriate
JavaScript in the OnClick event of the control.
• Tested only in Internet Explorer and FireFox. There is no alternative to
XmlHttp if it is not available for the browser.
• Session must be in "InProc" mode. "SQLServer" and "StateServer" modes
are not supported.

To sum up

I'm not going to get into the details of the inner workings of the classes. I have tried
to document every method, so anyone wanting to extend the AJAX controls that are
provided, I strongly recommend reading the comments of all the methods and the
classes. For the rest who just want to use the framework:

• The script path of the demo page is valid only if you extract the source files to
the wwwroot path. If you extract them to another directory you should
change the CallBackScriptPath of the application settings of web.config
accordingly.
• Follow the four steps that I have mentioned in the Using the code section of
this article.
• Read the comments of AjaxPage.cs, CallBackHelper.cs and
ICallBackEventHandler.cs.
• Read the comments of the public properties of AjaxPanel.
• Read the comments of the AjaxLinkButton control.
• You can send custom JavaScript to the client by using
CallBackHelper.Write.
• You must use the CallBackHelper.Request and CallBackHelper.Response
properties in the code of your page, unless it inherits from AjaxPage. The
same applies to your user controls and the AjaxUserControl that are
provided.
• Do not use Panel controls inside an AjaxPanel because even if only one child
of the Panel changes, all its children will have to be rendered on the client.
Use AjaxPanel controls instead.
• After the update of 23-9-2005, the framework can handle the
Response.Redirect and Server.Transfer methods during a callback. You
don't have to change them in your code.
• Use CallBackHelper.End instead of Response.End during a callback.
• Always keep in mind that the callback is different from postback, because the
page and its controls are persisted in the session, thus the Load event is not
raised, and you can add/remove controls from the AjaxPanel dynamically,
and the changes will be reflected on the browser. A bit like manipulating the
controls of a desktop application.

Points of interest

AjaxPanel handles the "Browser Back Button" problem as well. The "Browser Back
Button" problem is that by pressing the Back button, the browser loads the HTML
page by its cache, so any AJAX changes made to the page are lost, while the user
still expects to see the same page that he was viewing before.

To solve this, I have put in the page a JavaScript function that executes every time
the page is loaded and a hidden field that is empty. The function checks this hidden
field, and if it's empty, it assumes that the page was loaded by a request to the
server (i.e. by the Refresh button) and sets the value of the field. If the function
finds that the field is not empty, it assumes that the browser loaded the page by the
Back button (the value of the fields are then restored) and invokes a special callback
(CallBackStartup) on the server. When the CallBackStartup event is raised,
AjaxPanel renders all of its children on the client page, thus restoring the previous
page that the user was viewing.

Conclusion

I hope you find this framework useful. I encourage you to experiment with it, and if
some fancy, jaws-dropping, eyes-bleeding, amazing control comes out of it, please
share it with us.

You might also like