Click here to Skip to main content
Click here to Skip to main content

Using the .NET 2.0 Built-In Support for AJAX

, 22 Jun 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
This article shows how to create a simple AJAX enabled control using only the built-in features of .NET 2.0.

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:

Screenshot - salesData.jpg

The question now is how do we implement the red flag functionality? Using conventional strategies, we can do one of two things:

  1. Have the checkbox controls do an automatic post-back when they are clicked.
  2. 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:

  1. 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.
  2. The user clicks on a button, or performs some action that will execute a JavaScript function to initiate the AJAX callback.
  3. 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).
  4. The RaiseCallbackEvent method stores any results that should be passed back to the client in a class level string variable on the server.
  5. The client script calls the GetCallbackResult method and receives the string result from the server.
  6. 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
/// <span class="code-SummaryComment"><summary></span>
/// The delegate for the check changed event
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="value">The value of the check box.</param></span>

public delegate void CheckChangedDelegate(bool value);
#endregion
 
#region Events
/// <span class="code-SummaryComment"><summary></span>

/// The check changed event.

/// <span class="code-SummaryComment"></summary></span>

public event CheckChangedDelegate CheckChanged;
#endregion
 
#region On Check Changed
/// <span class="code-SummaryComment"><summary></span>

/// Execute the check changed event if it has been wired up.

/// <span class="code-SummaryComment"></summary></span>

/// <span class="code-SummaryComment"><param name="value">The value of the text box.</param></span>

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
{
      /// <span class="code-SummaryComment"><author />Hannes Foulds</author /></span>
      /// <span class="code-SummaryComment"><date>11 June 2007</date></span>
      /// <span class="code-SummaryComment"><summary></span>
      /// Test the ajax check box with event.
      /// <span class="code-SummaryComment"></summary></span>

      public class CheckTest : WebPart, INamingContainer
      {
            #region User Interface Elements
            /// <span class="code-SummaryComment"><summary>The AJAX textbox to test.</summary></span>

            protected AjaxCheckBox chkTest;
            #endregion
 
            #region Render
            /// <span class="code-SummaryComment"><summary></span>
            /// Render the control.
            /// <span class="code-SummaryComment"></summary></span>
            /// <span class="code-SummaryComment"><param name="writer">The writer to use for rendering.</param></span>

            protected override void Render(HtmlTextWriter writer)
            {
                  this.chkTest.RenderControl(writer);
            }
            #endregion
 
            #region Create Child Controls
            /// <span class="code-SummaryComment"><summary></span>

            /// Create the child controls.

            /// <span class="code-SummaryComment"></summary></span>

            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
            /// <span class="code-SummaryComment"><summary></span>

            /// The event handler for the check box change.

            /// <span class="code-SummaryComment"></summary></span>

            /// <span class="code-SummaryComment"><param name="value">The check box value.</param></span>

            void chkTest_CheckChanged(bool value)
            {
                  bool test = value;
                  
                  // perform logic here to do a database update

                  // with the value or do whatever you need to

                  // store the boolean

            }
            #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
/// <span class="code-SummaryComment"><summary></span>
/// Render the interface for the control.
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="writer">The writer used for rendering the control.</param></span>

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
/// <span class="code-SummaryComment"><summary></span>
/// Get the result of a client side callback.
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><returns>The callback result string.</returns></span>

public string GetCallbackResult()
{
      return null;
}
#endregion
 
#region Raise Callback Event
/// <span class="code-SummaryComment"><summary></span>

/// Raise the client callback event

/// <span class="code-SummaryComment"></summary></span>

/// <span class="code-SummaryComment"><param name="eventArgument">The event arguments.</param></span>

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
{
      /// <span class="code-SummaryComment"><author>Hannes Foulds</author></span>

      /// <span class="code-SummaryComment"><date>11 June 2007</date></span>

      /// <span class="code-SummaryComment"><summary></span>

      /// This control creates a ajax check box.

      /// <span class="code-SummaryComment"></summary></span>

      public class AjaxCheckBox : WebControl, INamingContainer, ICallbackEventHandler
      {
            #region Delegates
            /// <span class="code-SummaryComment"><summary></span>

            /// The delegate for the check changed event

            /// <span class="code-SummaryComment"></summary></span>

            /// <span class="code-SummaryComment"><param name="value">The value of the check box.</param></span>

            public delegate void CheckChangedDelegate(bool value);
            #endregion
 
            #region Events
            /// <span class="code-SummaryComment"><summary></span>

            /// The check changed event.

            /// <span class="code-SummaryComment"></summary></span>

            public event CheckChangedDelegate CheckChanged;
            #endregionregion
 
            #region Properties
            /// <span class="code-SummaryComment"><summary></span>

            /// The ID of the control that AJAX results should be displayed in.

            /// <span class="code-SummaryComment"></summary></span>

            protected string CheckBoxID
            {
                  get { return string.Concat(this.ClientID, "_result"); }
            }
            #endregion
 
            #region Render
            /// <span class="code-SummaryComment"><summary></span>

            /// Render the interface for the control.

            /// <span class="code-SummaryComment"></summary></span>

            /// <span class="code-SummaryComment"><param name="writer">The writer used for rendering the control.</param></span>

            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 Wink | ;-)

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Hannes Foulds
Web Developer
South Africa South Africa

Comments and Discussions

 
Question1 problem while using it in gridview Pinmembermanjots28-Jun-07 23:45 
GeneralNice job PinmemberMoim Hossain27-Jun-07 4:48 
GeneralThanks Pinmembermerlin98122-Jun-07 3:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141015.1 | Last Updated 22 Jun 2007
Article Copyright 2007 by Hannes Foulds
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid