Introduction
I have spent most of my professional career in a Microsoft SharePoint and Microsoft Content Management Server 2002 environment, and have learned to appreciate the value of custom WebControls – in SharePoint we have WebParts, which are just glorified WebControls, and in MCMS 2002, we have Placeholders, also just WebParts that have mutated.
Today, when I look at any custom piece of functionality that should be hosted either in SharePoint or on a normal .NET web page, I see things in terms of custom controls. Every custom control can contain many other standard and custom controls, and each of them can also contain child controls as needed. I think this is the same top down approach most of us use when writing code. We have methods, or methods call other methods, they can call more methods etc.
With the above being said, I am sure that you can infer that this article will be about custom web controls that are written using one of the .NET languages; my focus, however, is only to show how custom controls can be constructed to use some AJAX functionality with only the standard features provided by the .NET 2.0 Framework.
In this article, Microsoft ASP.NET AJAX (formally known as Microsoft Atlas) is not used in any form. Using the techniques described in this article, you can, for example, add AJAX functionality to your SharePoint 2007 web parts without the need to deploy any additional libraries – the approach will be to implement the ICallbackEventHandler
interface.
Check Box Example
In this article, I will construct a simple AJAX enabled check box, but let me first explain why a check box like this can be useful: assume we are creating a sales view control. This control will list all the sales that were made during the day (a potentially substantial list). When going through the list, the sales manager can "red flag" items that appear important to him. Later, he can apply a filter to view the entire list or only his short listed items. A sample interface might look like this:

The question now is how do we implement the red flag functionality? Using conventional strategies, we can do one of two things:
- Have the checkbox controls do an automatic post-back when they are clicked.
- Put a button at the bottom (and top) of the list, labeled Update, that will store the user's red flag selection when clicked.
Both of these approaches are less than optimal – if we have the entire page posted back and refreshed, we are going to have a very frustrated sales manager, sick of contending with constant page flashes, and tired of waiting for the page to finish loading every time he selects something. If we have a button to save the selection, the sales manager may forget to click it, close his browser, and when he later returns, find that he has to go through the entire list again, and re-select everything he is interested in, and hopefully remember to click on the Save button...
A much more user friendly interface will be where the user can just tick the check boxes for the items they are interested in, without the page posting back and without the need to remember clicking a Save button somewhere. To accomplish this, we will need the power of AJAX.
The AJAX Check Box
What I will be creating is a re-usable AJAX check box control that is not only relevant to the ACME sales view described above, but which can also be used as part of any custom control requiring AJAX check boxes.
Declaring the Check Box
Our control starts with the following declaration:
public class AjaxCheckBox : WebControl, INamingContainer, ICallbackEventHandler
The WebControl
and INamingContainer
interfaces are just the standard interfaces we always implement when creating custom controls, the important bit here is the ICallbackEventHandler
that we will use to AJAX enable the check box. To implement this interface, our control must have two functions, namely RaiseCallbackEvent
and GetCallbackResult
. The RaiseCallbackEvent
method is the method that will be executed on AJAX callbacks to the server, and the GetCallbackResult
method is used by the client browser to get a string of any data that resulted from the callback.
The typical logic that custom ICallbackEventHandler
AJAX controls use, is the following:
- On page load, the control renders itself as normal through the
Render
method. In the Render
method, a container control (like a div
tag) will also be rendered to the client, this container will display any AJAX results/dynamically created user interface. - The user clicks on a button, or performs some action that will execute a JavaScript function to initiate the AJAX callback.
- The server code calls the
RaiseCallbackEvent
method and performs the necessary processing (this method receives a string argument which is passed from the JavaScript method). - The
RaiseCallbackEvent
method stores any results that should be passed back to the client in a class level string variable on the server. - The client script calls the
GetCallbackResult
method and receives the string result from the server. - The client script processes the string result and omits the resulting HTML in the client container control (the
div
).
The check box created here will not do any client-side processing as there is nothing more to do on the client after the user has clicked a check box, but for an example of the complete model as described above, refer to the Validate
sample provided in the source code.
Check Box Events
I have implemented a custom event for the check box that will notify the control containing the check box whenever the value of the check box changes. This is actually a very neat part of the control – when RaiseCallbackEvent
is called, you can fire events from this method, and the listeners of the event will actually be notified of the event although it is an AJAX callback. The code below shows the relevant sections for the custom event (you will notice that there is nothing fancy here, this is standard code we know even from 2003 days):
#region Delegates
public delegate void CheckChangedDelegate(bool value);
#endregion
#region Events
public event CheckChangedDelegate CheckChanged;
#endregion
#region On Check Changed
protected void OnCheckChanged(bool value)
{
if (this.CheckChanged != null)
this.CheckChanged(value);
}
#endregion
In the sales example, our view control will thus have a few of these checkboxes and wire up event handlers for them. When the AJAX text box fires off its event, the sales view will update the database accordingly, blissfully unaware that the event was actually a result of an AJAX callback. Below, a simple control containing the AJAX check box is illustrated:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
namespace DrainCleaner.Controls
{
public class CheckTest : WebPart, INamingContainer
{
#region User Interface Elements
protected AjaxCheckBox chkTest;
#endregion
#region Render
protected override void Render(HtmlTextWriter writer)
{
this.chkTest.RenderControl(writer);
}
#endregion
#region Create Child Controls
protected override void CreateChildControls()
{
this.chkTest = new AjaxCheckBox();
this.chkTest.ID = "chkTest";
this.Controls.Add(this.chkTest);
this.chkTest.CheckChanged +=
new AjaxCheckBox.CheckChangedDelegate(chkTest_CheckChanged);
}
#endregion
#region Event Handlers
void chkTest_CheckChanged(bool value)
{
bool test = value;
}
#endregion
}
}
Doing the AJAX
The first thing I did was to render the user interface of the AJAX text box as shown below. You will notice that a server side check box is not created, and a simple <input>
tag is rendered. The client side onclick
event is responsible for calling the appropriate JavaScript function that will perform the AJAX callback.
The clientValue
in the example below is the client-side JavaScript that will be executed on an AJAX callback to get the value to pass to the server RaiseCallbackEvent
method, while the callbackRef
variable contains the string of the client-side AJAX JavaScript to execute for the callback.
#region Render
protected override void Render(HtmlTextWriter writer)
{
string clientValue = string.Format("document.getElementById('{0}').checked",
this.CheckBoxID);
string callbackRef = this.Page.ClientScript.GetCallbackEventReference(this,
clientValue, "null", null);
writer.WriteLine("<input id=\"{0}\" type=\"checkbox\" " +
"onclick=\"javascript:{1}\" />",
this.CheckBoxID, callbackRef);
}
#endregion
The final pieces of the puzzle are the implementation of the two functions which the ICallbackEventHandler
interface requires. The RaiseCallbackEvent
method parses the string value it receives from the client, and then calls the method that will execute any wired up events. The GetCallbackResult
method simply returns null
since no other client-side processing will be performed.
#region Get Callback Result
public string GetCallbackResult()
{
return null;
}
#endregion
#region Raise Callback Event
public void RaiseCallbackEvent(string eventArgument)
{
bool value = Boolean.Parse(eventArgument);
this.OnCheckChanged(value);
}
#endregion
Complete Code
To help put everything in perspective, I have provided the complete code for the AJAX check box below:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
namespace DrainCleaner.Controls
{
public class AjaxCheckBox : WebControl, INamingContainer, ICallbackEventHandler
{
#region Delegates
public delegate void CheckChangedDelegate(bool value);
#endregion
#region Events
public event CheckChangedDelegate CheckChanged;
#endregionregion
#region Properties
protected string CheckBoxID
{
get { return string.Concat(this.ClientID, "_result"); }
}
#endregion
#region Render
protected override void Render(HtmlTextWriter writer)
{
string clientValue = string.Format("document.getElementById('{0}').checked",
this.CheckBoxID);
string callbackRef = this.Page.ClientScript.GetCallbackEventReference(this,
clientValue, "null", null);
writer.WriteLine("<input id="\"{0}\"" onclick="\"javascript:{1}\"" />",
this.CheckBoxID, callbackRef);
}
#endregion
#region Get Callback Result
/// <summary>
/// Get the result of a client side callback.
/// </summary>
/// <returns>The callback result string.</returns>
public string GetCallbackResult()
{
return null;
}
#endregion
#region Raise Callback Event
/// <summary>
/// Raise the client callback event
/// </summary>
/// <param name="eventArgument">The event arguments.</param>
public void RaiseCallbackEvent(string eventArgument)
{
bool value = Boolean.Parse(eventArgument);
this.OnCheckChanged(value);
}
#endregion
#region On Check Changed
/// <summary>
/// Execute the check changed event if it has been wired up.
/// </summary>
/// <param name="value">The value of the text box.</param>
protected void OnCheckChanged(bool value)
{
if (this.CheckChanged != null)
this.CheckChanged(value);
}
#endregion
}
}
Conclusion
I hope that this article has at least provided you with something to think about, and if you still don't believe me that the event handler is executed, set a breakpoint at bool test = value;
and test this yourself ;-)
In the downloadable source code for this article, I have also included a validation control which does something a little more complex. I recently implemented a control like this that validated asset numbers against a database and it worked quite well.
AJAX is a really powerful technology, and I belief that it is here to stay as it definitely makes the user experience more pleasant by leaps and bounds. In future articles, I will have a look at ASP.NET AJAX and the wonders thereof.