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.
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>