Reporting server side operation progress with jQuery UI Progressbar

A colleague of mine recently had a problem with using jQuery UI Progressbar for reporting progress of asynchronous server side operations initialized via AJAX request. I decided to create a short sample for him and everybody else who might need it. I'm going to show how to do it in ASP.NET MVC and ASP.NET WebForms.
We will start with ASP.NET MVC sample. First thing to do is adding some JavaScript and CSS references:
<link href="<%= Url.Content("~/Content/jquery-ui-1.8.2.css") %>" rel="stylesheet" type="text/css" />
<script src="<%= Url.Content("~/Scripts/jquery-1.4.2.min.js") %>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/jquery-ui-1.8.2.min.js") %>" type="text/javascript"></script>
Markup for Progressbar is very simple (just one div), so we will need only two lines of HTML in this sample:
<button id="operation">Operation</button>
<div id="progressbar" style="width:500px"></div>
Now some interesting stuff begins. We need two controller actions. One for triggering actual process and one for reporting progress:
/// <summary>
/// Action for triggering long running operation
/// </summary>
/// <returns></returns>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Operation()
{
HttpSessionStateBase session = Session;

//Separate thread for long running operation
ThreadPool.QueueUserWorkItem(delegate
{
int operationProgress;
for (operationProgress = 0; operationProgress <= 100; operationProgress = operationProgress + 2)
{
session["OPERATION_PROGRESS"] = operationProgress;
Thread.Sleep(1000);
}
});

return Json(new { progress = 0 });
}

/// <summary>
/// Action for reporting operation progress
/// </summary>
/// <returns></returns>
[NoCache]
public ActionResult OperationProgress()
{
int operationProgress = 0;

if (Session["OPERATION_PROGRESS"] != null)
operationProgress = (int)Session["OPERATION_PROGRESS"];

return Json(new { progress = operationProgress }, JsonRequestBehavior.AllowGet);
}
What is missing is JavaScript, which will initialize Progressbar, trigger the operation and set timer for updating progress:
<script type="text/javascript">
$(document).ready(function () {
//Progressbar initialization
$("#progressbar").progressbar({ value: 0 });
//Button click event
$("#operation").click(function (e) {
//Disabling button
$("#operation").attr('disabled', 'disabled');
//Making sure that progress indicate 0
$("#progressbar").progressbar('value', 0);
//Perform POST for triggering long running operation
$.post('<%: Url.Action("Operation") %>', function (data) {
//Updating progress
$("#progressbar").progressbar('value', data.progress);
//Setting the timer
window.progressIntervalId = window.setInterval(function () {
//Getting current operation progress
$.get('<%: Url.Action("OperationProgress") %>', function (data) {
//Updating progress
$("#progressbar").progressbar('value', data.progress);
//If operation is complete
if (data.progress == 100) {
//Clear timer
window.clearInterval(window.progressIntervalId);
//Enable button
$("#operation").attr('disabled', '');
}
});
}, 500);
});
});
});
</script>
The result looks like this:
To make it work in ASP.NET WebForms, we need two PageMethods instead of actions:
/// <summary>
/// PageMethod for triggering long running operation
/// </summary>
/// <returns></returns>
[System.Web.Services.WebMethod(EnableSession=true)]
public static object Operation()
{
HttpSessionState session = HttpContext.Current.Session;

//Separate thread for long running operation
ThreadPool.QueueUserWorkItem(delegate
{
int operationProgress;
for (operationProgress = 0; operationProgress <= 100; operationProgress = operationProgress + 2)
{
session["OPERATION_PROGRESS"] = operationProgress;
Thread.Sleep(1000);
}
});

return new { progress = 0 };
}

/// <summary>
/// PageMethod for reporting progress
/// </summary>
/// <returns></returns>
[System.Web.Services.WebMethod(EnableSession = true)]
public static object OperationProgress()
{
int operationProgress = 0;

if (HttpContext.Current.Session["OPERATION_PROGRESS"] != null)
operationProgress = (int)HttpContext.Current.Session["OPERATION_PROGRESS"];

return new { progress = operationProgress };
}
and modified JavaScript:
<script type="text/javascript">
$(document).ready(function () {
//Progressbar initialization
$("#progressbar").progressbar({ value: 0 });
//Button click event
$("#operation").click(function (e) {
//Disabling button
$("#operation").attr('disabled', 'disabled');
//Making sure that progress indicate 0
$("#progressbar").progressbar('value', 0);
//Call PageMethod which triggers long running operation
PageMethods.Operation(function(result) {
if (result) {
//Updating progress
$("#progressbar").progressbar('value', result.progress)
//Setting the timer
window.progressIntervalId = window.setInterval(function () {
//Calling PageMethod for current progress
PageMethods.OperationProgress(function (result) {
//Updating progress
$("#progressbar").progressbar('value', result.progress)
//If operation is complete
if (result.progress == 100) {
//Clear timer
window.clearInterval(window.progressIntervalId);
//Enable button
$("#operation").attr('disabled', '');
}
});
}, 500);
}
});
});
});
</script>
The effect is pretty much the same:
The samples for ASP.NET MVC and ASP.NET WebForms can be downloaded here. Enjoy.