:::: MENU ::::

Sunday, June 29, 2008

Many times we are faced with heavy or slow processing pages in our projects. If we cannot improve performance, how do we display some sort of information in the browser while the page is processing?

I used to have this problem when accessing slow databases or retrieving a large amount of information from it. Sometimes, the queries lasted for 30 seconds or even more and it's not very user-friendly to just sit there waiting. Many impatient users will often assume that the application has hanged and close the browser before the query results are shown.

Let's imagine the following scenario. We have a form where the user specifies some sort of search pattern. He then clicks a button and the results are shown in a datagrid in the same page. Ok, solving this is easy...you just add some client scripting to the button that triggers the round-trip to the database so that on the client side a message will be displayed (something like: please wait...loading the data).

But what if you want to show the results in another aspx? By this I mean you access a page and on the page_load you retrieve the data and populate the datagrid? What if you have a complex form with lots of drop down lists that must be populated and you want to show some sort of info to your users while they are waiting for the page to appear on the browser? And, what if you want to do something like a dynamic bar to show that something is actually happening?

Well, I recently discovered a very useful method of the Response object. Because it functions like a Stream, you are able to say Flush several times during a request. What this means is that you don't have to wait for the entire page to be rendered to send the response down to the client. So, basically this looks like a good way of solving this problem: we create a mixed blend of HTML and JavaScript in the beginning of the Page_Load event, we flush it to the client and we proceed with our server-side processing. When we get the data we don't have to do anything special. The Page_Load event will end and the HTML will be rendered by ASP.NET. All we have to do is possibly hide the information we previously sent in order to display the page correctly.

I encapsulated the entire logic into a single class (C#):

using System;

using System.Web;

namespace WaitPage

{

/// <summary>

/// To use this class, simply insert in the beggining of your page load the following line:

/// Wait.Send_Wait_Info(Response);

///

/// Also, in the HTML part of your web form add the following line at the end of the head section:

/// <script>Stop_Wait();</script>

/// </summary>

public class Wait

{

public Wait()

{

}

const string MAIN_IMAGE = "images/logo.bmp";

const int PROGRESS_BAR_SIZE = 10; //number of steps in your progress bar

const string PROGRESS_BAR_STEP = "images/pro.bmp"; //image for idle steps

const string PROGRESS_BAR_ACTIVE_STEP = "images/pro2.bmp"; //image for active step

 

public static void Send_Wait_Info(System.Web.HttpResponse Response)

{

Response.Write("<div id=\"mydiv\" align=\"center\">");

Response.Write("<img src=\"" + MAIN_IMAGE + "\">");

Response.Write("<div id=\"mydiv2\" align=\"center\">");

for(int i=1;i<=PROGRESS_BAR_SIZE;i++)

{

Response.Write("<img id='pro" + i.ToString() + "' src='" + PROGRESS_BAR_STEP + "'>&nbsp;");

}

Response.Write("</div>");

Response.Write("</div>");

Response.Write("<script language=javascript>");

Response.Write("var counter=1;var countermax = " + PROGRESS_BAR_SIZE + ";function ShowWait()");

Response.Write("{ document.getElementById('pro' + counter).setAttribute(\"src\",\"" + PROGRESS_BAR_ACTIVE_STEP + "\"); if (counter == 1) document.getElementById('pro' + countermax).setAttribute(\"src\",\"" + PROGRESS_BAR_STEP + "\");else {var x=counter - 1; document.getElementById('pro' + x).setAttribute(\"src\",\"" + PROGRESS_BAR_STEP + "\");} counter++;if (counter > countermax) counter=1;}");

Response.Write("function Start_Wait(){mydiv.style.visibility = \"visible\";window.setInterval(\"ShowWait()\",1000);}");

Response.Write("function Stop_Wait(){ mydiv.style.visibility = \"hidden\";window.clearInterval();}");

Response.Write("Start_Wait();</script>");

Response.Flush();

}

}

}

This code will simply send some HTML and Javascript to the client creating an animated progress-like bar. You can download the source code of a demo app showing it's usage here.

The Stop_Wait script block must be inserted in the end of the head section. When the final response is sent, the bar is cleared and the JavaScript cycle that animates the bar is stopped. Pretty simple .

Also, all the images presented can easily be changed. There are 4 constants defined in the class.

  • The main image
  • The size of the bar (how many steps)
  • The image representing a step
  • The image representing the active step

so you can customize this at will. Also, I believe the code will help build your own mechanism. I just did it for the exercise so it's not very pretty :P

Hope this proves of some use to you.

More

Categories: