Tuesday, September 3, 2013

Lock ASP.NET Page and Show Animated Image While Waiting for a Long Post-Back

Introduction

Problem

One of the biggest problems in the web is pages that take a long time to process once submitted. Let’s say you have an ASP.NET form that takes up to 30 seconds to process and the user keeps pushing the submit button twice or more hoping to get the information faster (which can cause data problems). One way to solve this problem is by disabling the submit button once it’s been pushed. But what if you have more than one button on your form?

Solution

In this article, I will show you how you can disable all your ASP.NET form's controls during a post back and also show the user a nice friendly animated GIF while the page is posting back. In fact, you can even move this little box if you are bored waiting for it.

Yahoo to Yahoo

Yahoo gives us a full working framework of JavaScript that we can download and use in our web applications for free! So why not take advantage of it? You can find more information about it here.
In this article, I will only use the Panel object from the Yahoo YUI. Here are a few easy steps to get started:
Download the framework scripts to your project from here (v. 2.7).
The framework should be in one directory called YUI (I have downloaded the 2.7.0 version). Copy this directory to your web project, your web project should look like this:
WebProjectWithYUIFolder.gif

Show Me the Money Jerry!

Now that we have the Yahoo Framework setup in our project, we are in business! Here are a few easy steps to setup your ASP.NET page to take full advantage of the Panel object (here).
Under the <head> tag of HTML, include these JavaScript files:
<!-- YUI CSS -->
<link rel="stylesheet" type="text/css" href="yui/build/container/assets/container.css"/>
<link rel="stylesheet" type="text/css" href="yui/build/menu/assets/skins/sam/menu.css"/>
<!-- YUI Dependencies -->

<script type="text/javascript" src="yui/build/utilities/utilities.js" ></script> 
<script type="text/javascript" src="yui/build/container/container-min.js"></script>
<script type="text/javascript">
Let’s create few easy JavaScript functions: let’s start with initializing the Panel with a title that says "Loading, Please wait…" and have an animated GIF called Wait.gif (you can add more properties to this object, read the Yahoo docs).
// sets up all of the YUI dialog boxes
function InitDialogs() {
DialogBox_Loading = new YAHOO.widget.Panel("waitBox", 
 { fixedcenter: true, modal: true, visible: true, 
 width: "230px", close: false, draggable: true });
DialogBox_Loading.setHeader("Loading, please wait...");
DialogBox_Loading.setBody('<div style="text-align:center;">
 <img src="images/Wait.gif" id="Image1" /></div>');
DialogBox_Loading.render(document.body);
}
Let’s create a JavaScript function that is responsible to show the “wait box” or hide it based on a boolean value. We also want to make sure that the ASP.NET page is valid (all ASP.NET validation controls have succeeded) before we show the Panel object. We can use the Page_IsValid flag on the client side to check that. In order to get this flag, we are forced to call the JavaScript method generated by .NET called Page_ClientValidate(); on the push of the button. I will show this later in the article.
function Loading(b) {
    if (b == true && Page_IsValid == true) {

    DialogBox_Loading.show();
}

else {

    DialogBox_Loading.hide();
}
}
For this example, I have added the AJAX script manager so I can enjoy the pageLoaded() event on the client side. To read more about it, go here. If you don't want to use the Ajax Framework, then we need to wire up the page load event in JavaScript ourselves. Here is an example of how to do it: Let's add a helper JavaScript function that will call any function we want for a page load event. I have included that sample in my download sample project underYahooWaitLoadOnButtonServerNoAjax.aspx. Here is a working example of how to do it with JavaScript:
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}

// wire up the PageLoad function with our helper function addLoadEvent
addLoadEvent(function() {
pageLoad();
})
In the Page Load event, I will initialize the Dialog box and not show it:
function pageLoad() {
InitDialogs();
Loading(false);
}
Now we are ready to create a few ASP.NET controls, let’s create a text box, a Required Field Validator and an ASP.NET button that takes more than 30 seconds to process. We are going to link a few JavaScript calls to our button.
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="false" />
<div>

<asp:TextBox ID="txtFirstName" runat ="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="VtxtFirstName" ControlToValidate="txtFirstName" 
 ErrorMessage="error" runat="server"></asp:RequiredFieldValidator>
<asp:Button ID="btnPushLongJob" Text ="Start Long Job" runat="server" 
    OnClientClick="Page_ClientValidate();Loading(true);" OnClick="btnPushLongJob_Click"/>
</div>
</form>
</body>
I am calling Page_ClientValidate() which is a JavaScript function generated by my ASP.NET Validator control. I was forced to call it so it can set the Page_isValid flag on the client side which is used in Loading JavaScript function. And I am calling Loading(true) to show the Dialog box.
Now let’s plug a long processing task in our code behind for the event of Onclick of the button (remember to add the System.Threading namespace to enjoy the Thread class).
protected void btnPushLongJob_Click(object sender, EventArgs e)
{
        Thread.Sleep(30000);
}

Problem: GIF Animation Stops during Postbacks

Well, sometimes life is hard and not everything works as expected. For some reason, Internet Explorer decided to make our life a bit harder and stop all GIF animations during a post back, but there is a way around it and I will show it to you right here and now for free (I am a nice guy, no?). Resetting the source of the image in JavaScript seems to fix this issue.
I have added the following JavaScript code:
function UpdateImg(ctrl, imgsrc) {
var img = document.getElementById(ctrl);
img.src = imgsrc;
}
And I link my ASP.NET button to it from the code behind in the Page Load event.
protected void Page_Load(object sender, EventArgs e)
{
btnPushLongJob.Attributes.Add("onclick", 
 "setTimeout(\"UpdateImg('Image1','images/Wait.gif');\",50);");
}
And we are done!
Here is a full page code:
<%@ Page Language="C#" AutoEventWireup="true" 
 CodeFile="YahooWaitLoadOnButtonServerSide.aspx.cs" 
 Inherits="YahooWaitLoadOnButtonServerSide" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 
 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>

<!-- YUI CSS -->
<link rel="stylesheet" type="text/css" href="yui/build/container/assets/container.css"/>
<link rel="stylesheet" type="text/css" href="yui/build/menu/assets/skins/sam/menu.css"/>
<!-- YUI Dependencies -->

<script type="text/javascript" src="yui/build/utilities/utilities.js" ></script> 
<script type="text/javascript" src="yui/build/container/container-min.js"></script>
<script type="text/javascript">

function pageLoad() {
InitDialogs();
Loading(false);
}

function UpdateImg(ctrl, imgsrc) {
var img = document.getElementById(ctrl);
img.src = imgsrc;
}

// sets up all of the YUI dialog boxes
function InitDialogs() {
DialogBox_Loading = new YAHOO.widget.Panel("waitBox", 
 { fixedcenter: true, modal: true, visible: true, 
 width: "230px", close: false, draggable: true });
DialogBox_Loading.setHeader("Loading, please wait...");
DialogBox_Loading.setBody('<div style="text-align:center;">
 <img src="images/Wait.gif" id="Image1" /></div>');
DialogBox_Loading.render(document.body);
}
function Loading(b) {
if (b == true && Page_IsValid == true) {
DialogBox_Loading.show();
}
else {
DialogBox_Loading.hide();
}
}
</script>
</head>
<body>

<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="false" />
<div>

<asp:TextBox ID="txtFirstName" runat ="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="VtxtFirstName" 
 ControlToValidate="txtFirstName" ErrorMessage="error" 
 runat="server"></asp:RequiredFieldValidator>
<asp:Button ID="btnPushLongJob" Text ="Start Long Job" runat="server" 
    OnClientClick="Page_ClientValidate();Loading(true);" OnClick="btnPushLongJob_Click"/>
</div>
</form>
</body>
</html>
And here is the full code for the Code behind file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Threading;
public partial class YahooWaitLoadOnButtonServerSide : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
btnPushLongJob.Attributes.Add("onclick", 
 "setTimeout(\"UpdateImg('Image1','images/Wait.gif');\",50);");
}
protected void btnPushLongJob_Click(object sender, EventArgs e)
{
Thread.Sleep(3000);
}
}

The Code

I have created a sample project that includes the Yahoo YUI 2.7 Framework. You would want to try theYahooWaitLoadOnButtonServerSide.aspx page for this sample (for no Ajax support, viewYahooWaitLoadOnButtonServerNoAjax.aspx page). I have also included a few other samples like a tool tip and using the Dialog box with Page Methods.
You can download the code from the link at the top of this article.
I hope you enjoyed this article, and it made your life a little bit easier.


No comments:

Post a Comment