65.9K
CodeProject is changing. Read more.
Home

Another Simple Wait Page

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (7 votes)

Oct 11, 2006

CPOL

2 min read

viewsIcon

76486

downloadIcon

716

An article to demonstrate to use asynchronous delegate call and client script callback to build long wait page

Sample Image - screenshot.jpg

Introduction

Sometime you need to process a lengthy job in your web form. It is a better idea to build a wait page instead of letting your user just stare at the screen doing nothing.

Background

There are a lot of solutions over Internet to accomplish this purpose. My solution isn't new, the initial idea is from Brian Dunnington's article Building a Better Wait Page in code project. I have been using this approach for long time in ASP.NET 1.1. Since the release of ASP.NET 2.0, Microsoft makes a lot easier to implement client side callback, I decided to expand this solution to include AJAX function, so that we don't need to refresh the page from time to time.

Using the code

There are 3 main areas you need to consider in this solution

  • Asynchronous delegate call
  • Hook up with client callback
  • Client script

Asynchronous delegate call

    /// <summary>
    /// Definition of delegate
    /// </summary>
    /// <param name="minute"></param>
    /// <returns></returns>
    private delegate bool DoJobDelegate (int minute);

    /// <summary>
    /// To invoke the long process function
    /// </summary>
    /// <param name="minute"></param>
    /// <returns></returns>
    private IAsyncResult DoJobAsync(int minute) {
        DoJobDelegate doDelegate = new DoJobDelegate(doLongJob);
        IAsyncResult ar = doDelegate.BeginInvoke(minute, 
                          new AsyncCallback(MyCallback), null);
        return ar;
    }

    /// <summary>
    /// The server side callback handler
    /// </summary>
    /// <param name="ar"></param>
    private void MyCallback (IAsyncResult ar) {
        AsyncResult aResult = (AsyncResult)ar;
        DoJobDelegate doDelegate = (DoJobDelegate)aResult.AsyncDelegate;
        // Session object is used to tell if process finishes or not
        Session["NewOrderResult"] = doDelegate.EndInvoke(ar);
    }

    /// <summary>
    /// The main function to run long process
    /// </summary>
    /// <param name="minute"></param>
    /// <returns></returns>
    private bool doLongJob (int minute) {
        System.Threading.Thread.Sleep(minute * 1000 * 60);
        if (minute % 2 == 0) {
            return true;
        } else {
            return false;
        }
    }

Hook up with client callback

In order to hook with client callback, you have to implement ICallbackEventHandler interface

public partial class Process : System.Web.UI.Page, ICallbackEventHandler {
 protected string CallBackEventReference;
...
}

Then we need to prepare script to reference client function

        string ScriptRef = this.ClientScript.GetCallbackEventReference(
        this,
        "'CheckStatus'",
        "ClientCallBack",
        "this",
        "ClientCallBack",
        true);

        CallBackEventReference = ScriptRef;

This will create a client function like WebForm_DoCallback('__Page','CheckStatus',ClientCallBack,this,ClientCallBack,true);

From this point, you need to implement 2 functions of this interface

    /// <summary>
    /// Capture the event argument in this field, in this case I don't use it.
    /// </summary>
    string eventArgument = "";

    /// <summary>
    /// Returns result to client side
    /// </summary>
    /// <returns></returns>
    string ICallbackEventHandler.GetCallbackResult() {
        if (Session["NewOrderResult"] != null) {
            // after async call finishes,
            // it sets this Session object to some value,
            // this method will capture the status
            if (Convert.ToBoolean(Session["NewOrderResult"])) {
                return "even.htm";
            } else {
                return "odd.htm";
            }
        } else {
            return "";
        }
    }

    /// <summary>
    /// Gets parameter passed from client side
    /// </summary>
    /// <param name="eventArgument"></param>
    void ICallbackEventHandler.RaiseCallbackEvent (string eventArgument) {
        this.eventArgument = eventArgument;
    }

Client script

On the client side, we need to prepare some javascript functions with talk with server.

function ClientCallBack(Result, Context) {
    if (Result != "") {
        window.location.href = Result;
    }
}

In order to make broswer to check server process status, we also need a timer to make client script to run at some certain interval.

<body onload="startClock();">
// check server every 5 seconds, adjust this value to your own preference
var interval = 5
var x = interval
function startClock(){
    x = x - 1;
    setTimeout("startClock()", 1000);
    if(x == 0){
       <%= this.CallBackEventReference %>

So what is happnening now? The client script will be triggered every 5 seconds to talk with server. And ICallbackEventHandler.GetCallbackResult() method on server side will be called every 5 seconds. In this method it checks Session object value, if asynchronous call finishes the process, it will call the MyCallback to set Session object to a not null value returned from process result to make ICallbackEventHandler.GetCallbackResult() be able to capture the result.

Points of Interest

My first explore of client callback, I hope to add more features later.

History

  • 2006-10-11 Initial version.