Saturday, 25 June 2011

Implementing ICallbackEventHandler in ASP.NET Web Pages

In this post we will discuss about how to call Server methods from client side using ICallBackHandler.

Normally in ASP.NET, when any server control invoke any of its events it will do a PostBack to the server and show the response by refreshing the whole page. This process will make the user to wait for the response even for a simple functionality also, for example getting some values from the database and show to the user on the page when selecting a dropdown list.

In such kind of situations, we required to avoid the postback process and use some other method to fetch the values from the client without rendering / refreshing the whole page. To achieve this kind of requirement .NET provides various ways such as:

  1. Call a Web Service method from the client side (JavaScript or JQuery).
  2. Call a remote page to generate required response using ASP.NET AJAX concept.
  3. Call a method from the same page implementing ICallBackHandler.
  4. Call an HttpHanlder method from Javascript.
  5. Call the events and refresh the page using ASP.NET AJAX Extension controls (Update Panel) – This method doing normal Postback only, but will refresh the page in partial.
  6. Define server method using [AjaxMethod] attribute and use Ajax.NET library to call from client.
  7. Etc.,

There are some problems of using these methods – they are

  1. Whatever the modification done thro’ client side programming (using Javascript or JQuery) will not be retained when a normal post back happening to the server. There must be some workaround required to handle to retain the values if it required for further usage after post back happened.
  2. At any stage if the page redirected to next page and the user press browser back button, the modifications done thro’ Javascript will not be retained.
  3. Transferring the values processed at client side to server side and vice-versa will be a difficult process compare to Post Back (In post back View State will handle easily).

How CallBack Handler works:

Before going for actual implementation, let us understand some background of how callback handler concept works and how it differs from normal post back.

ASP.NET Postback
  • When postback happening the request goes to the server. The server will recreate the web page and controls for entire page and return the entire script to the client.
  • As it is Synchronized processing model, the use has to wait till all the processing done and refreshed the page.
  • The data which gets transferred from client to server and server to client is huge compare to call back method.

ASP.NET callback
  • When call back happening, it will go to the server and create only the part of the page and control and respond to the client.
  • As it is Asynchronous processing model, the user can continue with another job. Once the response received, the necessary action will be taken on the code.
  • Less data gets transferred from client and server.

So considering the performance and user experience, implementing CallBackHanlder will give better result. But as explained before, the UI changes done at client side will not be retained for further usage.

Below figure shows a simple explanation about how the process flow happening when calling a method from client side using ICallBackHanlder implemented page.

CallBackHanlder process flow

Implementation:

I have already blogged a post which explains about how to implement XML HTTP AJAX in ASP.NET pages. I am going the implement the same example which was used in that post for explaining with ICallBackHandler. It also helps to understand how these two concepts can be implemented using XML HTTP AJAX and ICallBackHandler.

The scenario of this implementation is used to create user details. In some of the websites, we have seen in the user creation module when user enters login Id there will be a message whether user Available or Not available based on the user details already stored in the database and these messages will show to the user by not refreshing the whole page. Mostly those functionalities are done with AJAX concepts.

The implementation follows:

Implement ICallbackEventHandler interface in the page and define variables in the page.

public partial class _Default : System.Web.UI.Page, ICallbackEventHandler
{
    /// 
    /// This variable is used to transfer messages from RaiseCallBackEvent to GetCallbackResult events
    /// 
    private String strMessage;

    /// 
    /// This variable holds the callback event. This method will be called from client side 
    /// WebForm_DoCallback('__Page',eventArg,UserIdCallBackResult,null,null,false)
    /// 
    public String UserIdCallbackEvent;
}
Register call back event in page load event.
protected void Page_Load(object sender, EventArgs e)
{
    RegisterCallBackEvents();
}
/// <summary>
/// This method is used for registering call back event
/// </summary>
private void RegisterCallBackEvents()
{
    UserIdCallbackEvent = this.ClientScript.GetCallbackEventReference
        (this, "eventArg", "UserIdCallBackResult", "null", "null", false);
}
Here UserIdCallBackResult is the method defined at client side (Javascript) which is used to call back once the process done at server and eventArg is a variable defined at the client side which holds the parameter value.

The ICallbackEventHandler interface has two methods which required to be implemented in the page.

  • RaiseCallbackEvent - This event is called when the call from client side (Javascript). This is the event to handle the call back handler. Here eventArgs is a parameter which is passed from client side.
  • GetCallbackResult - This methos returns the result of the callback event to client side.
/// <summary>
/// This event will be called when the callback event called from client side
/// </summary>
/// <param name="eventArg">This parameter is the argument parameter from the client side javascript </param>
void ICallbackEventHandler.RaiseCallbackEvent(string eventArg)
{
    // Checking Customer Id exist in the database or not
    bool IsCustomerExist = IsUserIdExist(eventArg.ToString().ToUpper());

    strMessage = IsCustomerExist.ToString();
}
//Return a string that will be received and processed
// by the clientCallback JavaScript funtion
String ICallbackEventHandler.GetCallbackResult()
{
    return strMessage;
}

In the aspx page, define two Javascript function. Those functions are:
  1. A Javascript function which will be called by a control (aspx or html control). The Javascript function will call the server using the UserIdCallbackEvent from the client.
  2. Another method for processing when the response comes from the server. This method will be defined in the code behind in the PageLoad event. In our code, that has been defined in RegisterCallBackEvents method. The runtime will call back this client method once the response ready from the server.
// This method will be called by a control, which will call the serve call back event
function UserNameCheck(id) {
    document.getElementById('spanAvailableStatus').style.display = "none";
    document.getElementById('spanProcessing').style.display = "block";
    var eventArg = document.getElementById(id).value;
    <%=UserIdCallbackEvent%>
}

// This is the function which handles the response comes from the server once server processing over.
// This is the call back function
function UserIdCallBackResult(strMessage)
{
    if (strMessage == 'True') {
        document.getElementById('spanAvailableStatus').innerHTML = 'Not Available';
        document.getElementById('spanAvailableStatus').className = 'Red';
    }
    else {
        document.getElementById('spanAvailableStatus').innerHTML = 'Available';
        document.getElementById('spanAvailableStatus').className = 'Green';
    }
    document.getElementById('spanAvailableStatus').style.display = "block";
    document.getElementById('spanProcessing').style.display = "none";
}

I am calling function from User Id text box lost focus for validating whether the user exist or not.
<asp:TextBox ID="txtUserId" runat="server" Width="120px" onchange="UserNameCheck(this.id)" style="float:left;" CssClass="TextBoxStyle">

Following methods are used for supporting our requirement in this example.
/// <summary>
/// To save the user details at xml file
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnSave_Click(object sender, EventArgs e)
{
    try
    {
        string strFileName = HttpContext.Current.Request.PhysicalApplicationPath + @"Data\UserDetails.xml";

        if (!File.Exists(strFileName))
        {
            XmlTextWriter textWritter = new XmlTextWriter(strFileName, null);
            textWritter.WriteStartDocument();
            textWritter.WriteStartElement("Users");
            textWritter.WriteEndElement();
            textWritter.Close();
        }

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(strFileName);

        XmlElement subRoot = xmlDoc.CreateElement("User");

        subRoot.AppendChild(CreateXMLElement(xmlDoc, "UserID", txtUserId.Text.Trim().ToUpper()));
        xmlDoc.DocumentElement.AppendChild(subRoot);

        subRoot.AppendChild(CreateXMLElement(xmlDoc, "FirstName", txtFirstName.Text.Trim()));
        xmlDoc.DocumentElement.AppendChild(subRoot);

        subRoot.AppendChild(CreateXMLElement(xmlDoc, "LastName", txtLastName.Text.Trim()));
        xmlDoc.DocumentElement.AppendChild(subRoot);

        subRoot.AppendChild(CreateXMLElement(xmlDoc, "MailId", txtMailId.Text.Trim()));
        xmlDoc.DocumentElement.AppendChild(subRoot);

        subRoot.AppendChild(CreateXMLElement(xmlDoc, "IsLocked", "1"));
        xmlDoc.DocumentElement.AppendChild(subRoot);

        xmlDoc.Save(strFileName);

        txtUserId.Text = "";
        txtFirstName.Text = "";
        txtLastName.Text = "";
        txtMailId.Text = "";
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

public XmlElement CreateXMLElement(XmlDocument xmlDoc, string name, string value)
{
    XmlElement xmlElement = xmlDoc.CreateElement(name);
    XmlText xmlText = xmlDoc.CreateTextNode(value);
    xmlElement.AppendChild(xmlText);
    return xmlElement;
}
/// <summary>
/// Method will load the XML and findout is the user exist or not
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public bool IsUserIdExist(string userId)
{
    try
    {
        // Sleep statement is for testing Processing image on the screen
        System.Threading.Thread.Sleep(1500);

        string strFileName = HttpContext.Current.Request.PhysicalApplicationPath + @"Data\UserDetails.xml";

        if (File.Exists(strFileName))
        {
            XPathDocument doc = new XPathDocument(strFileName);
            XPathNavigator nav = doc.CreateNavigator();
            XPathNodeIterator iterator;

            iterator = nav.Select(@"//User[UserID='" + userId + "']");

            while (iterator.MoveNext())
            {
                XPathNavigator nav2 = iterator.Current.Clone();

                if (nav2.Select(@"//Users").Current.SelectSingleNode("UserID").InnerXml.Length > 0)
                    return true;
            }
        }
        return false;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

My database is xml file, it would be like as following:
<?xml version="1.0" encoding="utf-8"?>
<Users>
  <User>
    <UserID>A-C71970F</UserID>
    <FirstName>Aria</FirstName>
    <LastName>Cruz</LastName>
    <MailId>Aria.Cruz@gmail.com</MailId>
    <IsLocked>1</IsLocked>
  </User>
  <User>
    <UserID>A-R89858F</UserID>
    <FirstName>Annette</FirstName>
    <LastName>Roulet</LastName>
    <MailId>Annette.Roulet@gmail.com</MailId>
    <IsLocked>1</IsLocked>
  </User>
</Users>

This code has been tested with IE 6.0/9.0, Firefox 3.6, Opera 11.01

You can see the output in video here



download the working example of the source code in C# here and in VB here

4 Responses to “Implementing ICallbackEventHandler in ASP.NET Web Pages”

  • Kikou says:
    27 January 2012 at 18:27

    Hi

    I've actually problem with ICallbackEventHandler and Firefox.
    Your article seem to be good, but i can't download the source code .
    How can I do?
    Thanks in advance
    Christian

  • Thiru says:
    30 January 2012 at 23:58

    Hi Kikou,
    I tested with FireFox v3.6.13 and v 6.0. Both works fine with me. Please let me know what version of Firefox you are using if you still face issue.

    You can contact me at pm.thirumalai@gmail.com for any queries.

  • Garry @ TriSys says:
    24 February 2012 at 17:17

    No code available in these links.

  • Thiru says:
    26 February 2012 at 23:12

    Hi Garry @ TriSys,

    Both the links are working fine when I tried. Please try once, if you still face issue let me know your mail id. So I can send you the source code by mail.

Post a Comment