Delay Load an UpdatePanel

Like most people, I use more than a few of Google's web applications on a day to day basis.  I think what I like best about their applications is the elegance of the user interface.  Just recently I started using their analytics web application to learn more about what drives visitors to my site.  One of the pages within the tool is a dashboard screen that displays a handful of different panels, each containing summary information pertaining to the web traffic for my site.  Some of the panels display charts and graphs, while others contain tables with aggregated data.   

kick it on DotNetKicks.com

As you can see, there is a lot of information being displayed on this page.  And as far as the user experience is concerned, this page is great.  It delay loads each of the panels instead of waiting until all of the data for all of the panels has been processed before serving the page.  As a result, the page loads almost immediately and the panels come to life one at a time as data becomes available (usually anywhere from 1 to 5 seconds).  So instead of waiting for 5 seconds with an empty screen, the shell of the page is immediately rendered and the panels are filled in as the data becomes available.  It turns out the project I am currently working on could benefit from this approach, so I have been playing around with using ASP.NET AJAX and the AjaxControlToolkit to add similar behavior to our dashboard pages.

For a proof of concept, I implemented a test page that delay loads a GridView full of customers from the Northwind database.  You can view the live demo here.

The basic approach I used was to start refreshing the UpdatePanel from the ASP.NET AJAX client side pageLoad event.  To accomplish this, I added an asp Button to the page, set its style to hidden, and wired the UpdatePanel to conditionally update when this button causes a postback (the technique is outlined in more detail here).  Then from the pageLoad event, I start the asynchronous post-back to fetch the data from the server.  After the panel is loaded, it behaves just like it would normally, you can sort and page through the results just like you would expect.

var _isInitialLoad = true;

function pageLoad(sender, args){
    if(_isInitialLoad){
        _isInitialLoad = false;
        //  simulate a button click by forcing the postback
        //  causing the updatepanel to update
        __doPostBack('<%= this.btn.ClientID %>','');        
    }
}

Here is the complete source for the page.  You will need to replace the connection string with a valid northwind connection before you can run the sample locally.

<%@ Page Language="C#" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Load(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(3000);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    protected void Btn_Click(object sender, EventArgs args)
    {
        this.gvCustomers.DataSourceID = this.sqldsCustomers.ID;
        this.gvCustomers.DataBind();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Untitled Page</title>
    <script type="text/javascript" language="javascript">
    
    var _isInitialLoad = true;

    function pageLoad(sender, args){
        if(_isInitialLoad){
            _isInitialLoad = false;
            //  simulate a button click by forcing the postback
            //  causing the updatepanel to update
            __doPostBack('<%= this.btn.ClientID %>','');        
        }
    }
    
    function onUpdating(){
        // get the update progress div
        var updateProgressDiv = $get('updateProgressDiv'); 

        //  get the gridview element        
        var gridView = $get('<%= this.gvCustomers.ClientID %>');
        
        // make it visible
        updateProgressDiv.style.display = '';        
        
        // get the bounds of both the gridview and the progress div
        var gridViewBounds = Sys.UI.DomElement.getBounds(gridView);
        var updateProgressDivBounds = Sys.UI.DomElement.getBounds(updateProgressDiv);
        
        //    do the math to figure out where to position the element
        //  top right of gridview
        var x = (gridViewBounds.x + gridViewBounds.width - updateProgressDivBounds.width);
        var y = gridViewBounds.y;

        //    set the progress element to this position
        Sys.UI.DomElement.setLocation (updateProgressDiv, x, y);           
    }

    function onUpdated() {
        // get the update progress div
        var updateProgressDiv = $get('updateProgressDiv'); 
        // make it invisible
        updateProgressDiv.style.display = 'none';
    }
    
    </script>

</head>
<body>
    <form id="form" runat="server">
        <asp:ScriptManager ID="scriptManager" runat="server" />
        <div>
            <asp:SqlDataSource ID="sqldsCustomers" runat="server" 
                SelectCommand="select customerid, companyname, contactname, contacttitle from dbo.customers"
                SelectCommandType="Text" ConnectionString="todo" />
            <asp:SqlDataSource ID="sqldsCustomersEmpty" runat="server" 
                SelectCommand="select top 10 '' as customerid, '' as companyname, '' as contactname, '' as contacttitle from dbo.customers"
                SelectCommandType="Text" ConnectionString="todo" />                
            
            <p style="background-color:AliceBlue; width:95%">
                Example of delay loading a panel using ASP.NET AJAX
            </p>
            
            <br />
            <asp:Button ID="btn" runat="server" OnClick="Btn_Click" style="display:none;"/>   
            <asp:Label ID="lblTitle" runat="server" Text="Customers" BackColor="lightblue" Width="95%" />
            <asp:UpdatePanel ID="updatePanel" runat="server" UpdateMode="Conditional">
                <Triggers>
                    <asp:AsyncPostBackTrigger ControlID="btn" />
                </Triggers>
                <ContentTemplate>
                    <asp:GridView ID="gvCustomers" runat="server" DataSourceID="sqldsCustomersEmpty" 
                            AllowPaging="true" AllowSorting="true" PageSize="10" Width="95%">
                        <AlternatingRowStyle BackColor="aliceBlue" />
                        <HeaderStyle HorizontalAlign="Left" />
                    </asp:GridView>
                </ContentTemplate>
            </asp:UpdatePanel>  
            <ajaxToolkit:UpdatePanelAnimationExtender ID="upae" BehaviorID="animation" runat="server" TargetControlID="updatePanel">
                <Animations>
                    <OnUpdating>
                        <Parallel duration="0">
                            <%-- place the update progress div over the gridview control --%>
                            <ScriptAction Script="onUpdating();" />  
                         </Parallel>
                    </OnUpdating>
                    <OnUpdated>
                        <Parallel duration="0">
                            <%--find the update progress div and place it over the gridview control--%>
                            <ScriptAction Script="onUpdated();" /> 
                        </Parallel> 
                    </OnUpdated>
                </Animations>
            </ajaxToolkit:UpdatePanelAnimationExtender>           
            <div id="updateProgressDiv" style="background-color:#CF4342; display:none; position:absolute;">
                <span style="color:#fff; margin:3px">Loading ...</span>
            </div>
        </div>
    </form>
</body>
</html>


TrackBack

TrackBack URL for this entry:
http://mattberseth.com/blog-mt/mt-tb.fcgi/37

Listed below are links to weblogs that reference Delay Load an UpdatePanel:

» Delay Load an UpdatePanel from DotNetKicks.com
You've been kicked (a good thing) - Trackback from DotNetKicks.com [Read More]

» Using javascript on pages that use a master page from ASP.NET AJAX Forum Posts
I was wondering if i could get any guidance on using javascript for pages that make use of a masterpage [Read More]

Comments


You mentioned that you are using a hidden button and JavaScript to trigger the UpdatePanel to refresh itself - per Simones tip. However, I noticed that you didnt set an EventName attribute on the AsyncPostBackTrigger element.

Any particular reason?

I ask because Ive been having some trouble getting that technique to work for my application. It seems that firing the event from JavaScript causes the page lifecycle to start (as I hit the Page_Init step) but never make it into the event handler code.

Ive tried wiring up the even declaratively and in the Init handler - but to no avail.

Anyhow, just wondering if you omitted the EventName for a reason.

Thanks!

Posted by: Jayaveer on July 20, 2007 12:00 AM

Hi,
How can we do the same for multiple grids, i mean differed loading.
I tried to update two updatepanels by using this code
__doPostBack(,);
__doPostBack(,);
But the problem is only the last updatepanel gets teh data back from server.
Can i know how to handle this situation?

Thanks,

Posted by: Shams on August 6, 2007 12:00 AM

Hi Matt, Its a great post! I tried implementing this into a content page and it doesnt load the gridview! It shows the loading animation and just sticks there. Can you let me know if theres anything to change to work it in content pages? Thanks!

Posted by: Jan Kokenberg on October 17, 2007 12:00 AM

Hi Matt. Great post! Ive implemented your example in a website Im currently working on.

Im running into a bit of trouble however, when the Loading... text is shown it messes up the layout of the page. It doesnt do this everytime, but quite regularly.

Im trying to delay load a datalist with 4 columns...

@Shams -

If you replace

__doPostBack(,);

with

__doPostBack(,);

in your pageLoad method, that should fix the problem youre having with Master/Content pages.

- Brad

I have the same problem:
"Hi Matt, Its a great post! I tried implementing this into a content page and it doesnt load the gridview! It shows the loading animation and just sticks there. Can you let me know if theres anything to change to work it in content pages? Thanks!"

Great articles; loads panel with that fancy manner is awesome, pageflakes loads like that, Do they use the code like yours ??

Posted by: gene on December 17, 2007 12:00 AM

I had a similar issue with the UpdateProgress getting triggered but the UpdatePanel not reloading. However, using

var button = $get();
button.click();

instead of __doPostBack()... was able to get this to work. Thanks Matt.

Posted by: Ali Zaidi on February 7, 2008 12:00 AM

great work matt.

i am your fan :)

keep it up.

hallo matt,
thats exactly what i was looking for.
I wonder, why this functionality is not implemented on a easier way in ajax-contols.

regards
raimund

Posted by: Tim on May 1, 2008 12:00 AM

I really like this article and its something that Ive been wanting to do. However Im having a little trouble getting it to work I think because Im not using the asp:SqlDataSource and am inplace using compiled code and an OnRowDataBind for my grid view.

Here si how Im binding my GridView currently (just slimmed down a little)
protected void Page_Load(object sender, EventArgs e)
{
Biz.Collection oData = new Biz.Collection();
oData.OpenAll();
GridView1.DataSource = oData;
GridView1.DataBind();

}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Biz.Project oData = e.Row.DataItem as Biz.Project;
Label lblName = e.Row.FindControl("lblName") as Label;
if (lblName != null)
lblName.Text = oData.Name;
}
}

And of course I have the GridView on the front end with a OnRowDataBound="GridView1_RowDataBound" not the datasource ID like you are using.

Any tips on how this could be done, all it does is sit at Loading... when I try to add in your code.

Thanks for any help you might give.
Tim

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Consulting Services

Yep - I also offer consulting services. And heck, I'll do just about anything. If you enjoy my blog just drop me an email describing the work you need done.

Recent Comments

  • Tim wrote: I really like this article and its something that Ive been wanting to do. However Im having a little...
  • Raimund Popp wrote: hallo matt, thats exactly what i was looking for. I wonder, why this functionality is not implemente...
  • Ali Zaidi wrote: great work matt. i am your fan :) keep it up. ...
  • gene wrote: I had a similar issue with the UpdateProgress getting triggered but the UpdatePanel not reloading. H...
  • Arturo wrote: Great articles; loads panel with that fancy manner is awesome, pageflakes loads like that, Do they u...
  • victorantos wrote: I have the same problem: "Hi Matt, Its a great post! I tried implementing this into a content page ...
  • Brad Mellen-Crandell wrote: @Shams - If you replace __doPostBack(,); with __doPostBack(,); in your pageLo...
  • Jan Kokenberg wrote: Hi Matt. Great post! Ive implemented your example in a website Im currently working on. Im running...