SingingEels : Development Community & Resource

Login

Articles

  • ADO.NET (2)
  • ASP.NET (31)
  • LINQ (4)
  • Security (2)
  • Silverlight (2)
  • SQL (7)
  • Standards (5)
  • WCF (1)

Syndication

  • Articles RSS
  • Blogs RSS

Contribute

  • Our Authors List
  • Member Sign-Up
  • Suggestions Box

Real-Time Progress Bar With ASP.NET AJAX

(Mar 26 2008 - 05:02:46 AM by Timothy Khouri) - [print article]

Due to the disconnected nature of the web, developers have often wondered how to display certain metrics such as a real-time progress bar. This article will show you how to easily build a real progress bar that actually reports progress of a process running on the server using ASP.NET AJAX.

Our Scenario

First, let's define our scenario so that we're all on the same page. We'll pretend that we have made a portal (web application) that allows the local IT administrators to run a task on each computer in the office. Since this task takes a variable amount of time, they want to see a real-time progress bar showing how much further the task has to be done.

Now, I don't want to lose you in the scenario, so I'll tell you up front that we are going to completely fake the "run some code on another machine" stuff. Try to keep focus on the fact that we are talking about using ASP.NET AJAX and how to report progress cleanly back to the client.

Our Needs vs ASP.NET AJAX

Let's talk about some of our needs, and how they are solved by ASP.NET AJAX. First of all, like we mentioned before the nature of the web is 'disconnected'. This means that once you write your data to the client (in our case the amount of progress that has passed), the connection is closed, so you can't update values later in the same request).

We will solve this need by implimenting a "polling" architecture by consuming an embeded webservice every second. This may seem like a heavy amount of data being transfered, but it's not. This is because consuming web services in ASP.NET AJAX is a very light weight request as it is not posting or rendering the entire page. As a side note, this is different than when you simply wrap your site in an "UpdatePanel", which *will* upload the entire view state of the page.

The second need we will have is to perform a custom action when the progress is updated. For simplicities sake, we're just going to display the current progress percentage on the page. Instead of messing up each page with a lot of JavaScript, we'll encapsulate the client code into a custom "script control" that has a method we can tap into to perform our desired functionality.

ScriptControls are just regular WebControls. The difference is that they use the ASP.NET AJAX framework to associate JavaScript code with the rendered control. Think about it as if it's a "code-behind" file... that's not "behind", but rather on the client.

Making Our Web Service

Let's start by making the two web service methods that we're going to need for this example. I'm not going to get into too much detail on creating and consuming webservices. If you're not familiar with consuming web services in ASP.NET AJAX, then I suggestion you look at this article: Consuming Web Services With ASP.NET AJAX.

So, as is detailed in that article, I'm going to add my "asmx file" (ASP.NET WebService) to my web application project, and my code behind file will look like this:

using System;
using System.Web.Services;
using System.Web.Script.Services;

namespace SingingEels
{
   [ScriptService]
   public class DoStuffWebService : WebService
   {
       [WebMethod, ScriptMethod]
       public void BeginProcess(string processName)
       {
           // Here is where I'm starting my timers...

       }

       [WebMethod, ScriptMethod]
       public int GetProcessProgress(string processName)
       {
           // Here is where I return an integer between 0 and 100

           // that tells the progress of the process by name.

       }
   }
}

The actual code that I'm doing in each of those functions is not important for this article, so I'm intentionally not showing it so that we don't fall off track due to ugly code :P The point to keep in mind here is that, as was mentioned in the article above, web services are easy to create and consume with ASP.NET AJAX.

Polling With AJAX

All that's left now is that we impliment our "ScriptControl" on our page, and wire up a method that tells the browser when to start polling for progress updates, and when to stop (as well as what to do when progress changes). The way I'm going to handle this is by putting most of the code into my ScriptControl. My reasons for doing this are 2 fold:

  1. To segragate the control-specific code into it's own JavaScript file (just like we do with code behind files).
  2. To make it easy to add this progress bar control to another page with only a minimal amount of setup.

I'm going to give my control 2 properties that need to be set. The first property is the "ProcessName" that will be run on the server. I'm simply using this as a "key" so that when I return to the server, I'll be getting the progress of that running process. The second property will indicate the interval (time between polling).

At the end of this article, you can download the entire source code for the project (which is a Visual Studio 2008 Web Application project). But for now, I'll skip ahead and show you how to impliment this control, and what the results will look like:

<div>
   <button onclick="$get('progressBar1').Start();">
       Process 1</button>
   <SingingEels:ProgressBar ID="progressBar1" runat="server" PollInterval="250" ProcessName="Format Hard Drive" />
</div>
<div>
   <button onclick="$get('progressBar2').Start();">
       Process 2</button>
   <SingingEels:ProgressBar ID="progressBar2" runat="server" PollInterval="500" ProcessName="Defrag Computer" />
</div>
<div>
   <button onclick="$get('progressBar3').Start();">
       Process 3</button>
   <SingingEels:ProgressBar ID="progressBar3" runat="server" PollInterval="250" ProcessName="Email Mom" />
</div>

Notice that on our page we don't need to write our JavaScript for doing the polling, updating or anything. Again, this is because we are making a ScriptControl and associating our control with a JavaScript code file. So, let's look at the code in the JS file.

// Register the namespace for the control.
Type.registerNamespace("SingingEels");

SingingEels.ProgressBar = function(element) {
   SingingEels.ProgressBar.initializeBase(this, [element]);

   this._pollInterval = 0;

   this._processName = null;
}

Because my "ProgressBar" custom control is in the "SingingEels" namespace, I have to first register that namespace with ASP.NET AJAX. Then, I have a simple constructor for our client side representation of the control. By the way, most of this is not hand written, but rather if you add a "AJAX Client Control" to your web app, it will come with a template of JS code that you can use.

Add "AJAX Client Control" dialog

Now back to our code:

SingingEels.ProgressBar.prototype = {
   "initialize" : function() {
       SingingEels.ProgressBar.callBaseMethod(this, "initialize");

       // I'm going to stick the properties on the element itself so

       //that I can just pass it as a parameter to my method.

       this._element.PollInterval = this._pollInterval;

       this._element.ProcessName= this._processName;

       // Here is my polling function...

       var doPolling = function(element) {
           // This is the web service that I'm calling. Yes I realize

           // that I'm hard coding it in here... but this article is

           // about the progress bar, not about web services :)

           SingingEels.DoStuffWebService.GetProcessProgress(element.ProcessName, function(result) {
               // Update the span with the current progress...

               element.innerHTML = result + "%";

               // If we're not done, then re-poll in "X" milliseconds.

               if (result < 100) {
                   setTimeout(function(){doPolling(element);}, element.PollInterval);
               }
           });
       }

       // I'm going to add a function right to the element itself called

       // "Start". This way you can just call $get('blah').Start();

       this._element.Start = function() {
           SingingEels.DoStuffWebService.BeginProcess(this.ProcessName);

           doPolling(this);
       }
   },

/* ... There's a little more code, but it's just a few properties and another "Register with AJAX" snippet... You can see all of that when you download the source if you want to... */

The Results

When this is all said and done, if you click the buttons (in any order, and you can wait as long as you want), you'll see something like this as the result:

A real-time progress bar using ASP.NET AJAX

Isn't that BEAUTIFUL? If you're complaining about the fact that it's not styled, or about how it's not really a progress "bar" but rather a progress "text display", then download the source and fix it yourself.

You can of course get creative with the progress displays... for instance instead of displaying a percentage of some progress, you could display other results that are updating on the server. An example of that would be displaying the current CPU and RAM usage on you're server. Here's the source code: SingingEels_RealTimeProgressBar.zip

  • Mar 28 2008 - 05:44:19 AM Mark Smith

    Great article Timothy, and it's nice to see how it's done correctly rather than just slapping an UpdatePanel on the page with a Timer attached to it (which I will admit to doing once!).

  • Apr 17 2008 - 02:34:27 AM kruelintent

    Hi,

    I like this solution, very neat. Just wondering on a couple of things though...

    if I have a normal <asp:button and I set the onclientclick where the normal ones have onclick it doesn't like it...

    Also. If I keep the buttons as normal HTML ones (which is fine I can always postback to the server from the js) then I still need to be able to poll my server side process to get actual progress.. How would you go about getting this from the asmx file?

    Hope you can help.

    Thanks

    Tom.

  • May 12 2008 - 04:56:49 AM Timothy Khouri

    Hey Tom,

    The asp:Button approach would work fine as long as the button has runat="server", and that in the "OnClientClick" method you remember to 'return false;'. That way the button won't post back to the server (which would cause a refresh).

    If you wanted to post back to the server for some reason, but keep displaying your progress bars when you got back, you could simply set a flag and recall the polling function with the same parameters that you started with. (When I say "flag" I mean a hidden input, or a cookie or something like that).

  • May 13 2008 - 05:29:53 PM jenkoolkat

    Great Article. In fact I just discovered this blog and have now read and downloaded 2 of your code samples. Your samples work and are very easy to understand. I will try and make time to read many more of your articles. I have used AJAX but have not written my own classes yet, there is so much more to learn. Thanks for your help.

You must be logged in to add comments. If you have not already done so, you can create an account here. If you already are a member, you first need to login before you can comment.

Developer / Architect / Author

People to Follow

Experts in the categories related to this article.

  • Jonathan Carter

Related Blogs

These are the most recent blog posts related to this article.

  • How to Handle "Side Content" in ASP.NET MVC
  • LINQ to SQL - Am I Hitting The Database?
  • ASP.NET MVC - Issue with CSS Class Name on ActionLinks
  • Site Updates, Bug Fixes and Syndication in .NET 3.5

Related Ads

SingingEels.com as of Aug 21 2008 - 12:36:04 AM - (0.1875)