Search
Close this search box.

Asp.net Ajax UpdatePanel Simultaneous Update – A Remedy

If you ever try to do more than one simultaneous partial update with Asp.net Ajax Update Panel, I guess  you already found  that Asp.net Ajax Framework cancel the current update request and starts the new one. You think I am kidding? Okay lets try the following code:

<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            lblDate1.Text = lblDate2.Text = DateTime.Now.ToString();
        }
    }

    protected void btnDate1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate1.Text = DateTime.Now.ToString();
    }

    protected void btnDate2_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate2.Text = DateTime.Now.ToString();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Update Panel</title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:UpdatePanel ID="updDate1" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                Update Panel 1: <asp:Label ID="lblDate1" runat="server"></asp:Label>
                <asp:Button ID="btnDate1" runat="server" Text="Update Time" OnClick="btnDate1_Click" />
            </ContentTemplate>
        </asp:UpdatePanel>
        <div>
            <asp:UpdatePanel ID="updDate2" runat="server" UpdateMode="Conditional" RenderMode="Inline">
                <ContentTemplate>
                    Update Panel 2: <asp:Label ID="lblDate2" runat="server"></asp:Label>
                    <asp:Button ID="btnDate2" runat="server" Text="Update Time" OnClick="btnDate2_Click" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </div>
    </form>
</body>
</html>

The example is quite simple, I have two update panel which UpdateMode is set to Conditional, upon clicking the buttons inside the update panel I am doing a fake delay of 5 seconds then printing the current time of the server in the labels. Okay now run the code and click the first button and then click the second button in 5 seconds so that you can do simultaneous update. What happens, the first label never gets updated? Yes this is the nature of update panel, it cancels the current call and start executing the new one. You can get a more clear picture if you run it in FireFox with FireBug, Open FireFox Click Tools->FireBug->Open FireBug, then move to the Console tab in the firebug and repeat the button clicks, you will find only the last request spinning  animation gets completed.

I can understand the Update Panel does a monsters job for us, executing the full life cycle of the page in the server, generating the updated part of the page, downloading new scripts, updating viewstates etc and that is why it needs to work serially. But how come it discards it current update request once it found a new request? is not it better if it queues the new request and execute it once the current request completes?

Now Consider the following page:

<%@ Page Language="C#"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            lblDate1.Text = lblDate2.Text = DateTime.Now.ToString();
        }
    }

    protected void btnDate1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate1.Text = DateTime.Now.ToString();
    }

    protected void btnDate2_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate2.Text = DateTime.Now.ToString();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Update Panel Enhanced</title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Path="~/Test/PageRequestManagerEx.js" />
            </Scripts>
        </asp:ScriptManager>
        <script type="text/javascript">
            PageRequestManagerEx.init();
        </script>
        <asp:UpdatePanel ID="updDate1" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                Update Panel 1: <asp:Label ID="lblDate1" runat="server"></asp:Label>
                <asp:Button ID="btnDate1" runat="server" Text="Update Time" OnClick="btnDate1_Click" />
            </ContentTemplate>
        </asp:UpdatePanel>
        <div>
            <asp:UpdatePanel ID="updDate2" runat="server" UpdateMode="Conditional" RenderMode="Inline">
                <ContentTemplate>
                    Update Panel 2: <asp:Label ID="lblDate2" runat="server"></asp:Label>
                    <asp:Button ID="btnDate2" runat="server" Text="Update Time" OnClick="btnDate2_Click" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </div>
    </form>
</body>
</html>

The only difference you will find from the previous one that I have added a new JavaScript file PageRequestManagerEx.js and called a method PageRequestManagerEx.init(). Now run this page and repeat the button clicks, you will find the complete source in the bottom of this post. If you run it in FireFox with FireBug, you will find that no matter how many clicks you made in those buttons there is only one concurrent call and once the current call completes it executes another call. Yes as you have guessed the new JavaScript file which I just added does the trick. Now let us see what is in the JavaScript file.

var PageRequestManagerEx = {
  _initialized : false,

  init : function() {
    if (!PageRequestManagerEx._initialized) { var _callQueue = new Array();
var _executingElement = null;
var prm = Sys.WebForms.PageRequestManager.getInstance();

_prm.add_initializeRequest(initializeRequest);
_prm.add_endRequest(endRequest);

PageRequestManagerEx._initialized = true;
}

function initializeRequest(sender, args) {
  if (_prm.get_isInAsyncPostBack()) {
    // if we are here that means there already a call pending.

    // Get the element which cause the postback
    var postBackElement = args.get_postBackElement();

    // We need to check this otherwise it will abort the request which we made
    // from the end request
    if (_executingElement != postBackElement) {
      // Does not match which means it is another control
      // which request the update, so cancel it temporary and
      // add it in the call queue
      args.set_cancel(true);
      Array.enqueue(_callQueue, postBackElement);
    }

    // Reset it as we are done with our matching
    _executingElement = null;
  }
}

The PageRequestManagerEx is a Static class which hooks the initializeRequest and endRequest events of the original PageRequestManager. Then in the initializeRequest it first checks if there is an ongoing update, if so it cancel the new request and adds the control in a queue which cause the postback. Then in the endRequest it check if there is any pending call in the queue, if so it executes it. This loops gets executed until all the call in the queue get completed.

Lets me know if you found any issue. I will  be also very curious to know why update panel does not behave like this?

Download : Complete Source

kick it on DotNetKicks.com

posted on Wednesday, August 08, 2007 6:29 AM

This article is part of the GWB Archives. Original Author: Amit’s Blog

Related Posts