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 Simone's tip. However, I noticed that you didn't set an EventName attribute on the AsyncPostBackTrigger element.

Any particular reason?

I ask because I've 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.

I've 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!

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,

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

Hi Matt. Great post! I've implemented your example in a website I'm currently working on.

I'm running into a bit of trouble however, when the 'Loading...' text is shown it messes up the layout of the page. It doesn't do this everytime, but quite regularly.

I'm 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 you're 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 doesn't load the gridview! It shows the loading animation and just sticks there. Can you let me know if there's 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 ??

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.

great work matt.

i am your fan :)

keep it up.

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

regards
raimund

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

Here si how I'm 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.)

This Blog

  • Email Me
  • RSS
  • Atom
  • Entries - 102
  • Comments - 1276
  • Recent Comments

    • Tim wrote: I really like this article and it's something that I've been wanting to do. However I'm having a lit...
    • Raimund Popp wrote: hallo matt, that's exactly what i was looking for. I wonder, why this functionality is not implement...
    • 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 you...
    • Jan Kokenberg wrote: Hi Matt. Great post! I've implemented your example in a website I'm currently working on. I'm runn...