Showing progress of a long running operation and keep screen up to date with what is happen in the server side is one common tasks one of a common task in web development. In this scenario, I have used UpdatePanel to wrap the bit I need to update and then a separate thread as the worker. Simply in the button click event of the Do Task button, I start a thread and hand over the long running task to the thread execution
Thread thread = new Thread(new ThreadStart(SomeLongOperation)); thread.Start();And also I set the progress bar visible true and set up initial message. As well as I have couple of flags in the Session to tell that the state of the page. They are Processing and Completed. I set Processing flag to true and Completed flag to false and register start-up script to refresh the page in a while.
this.lblMessage.Text = "<br />Processing... Please wait..."; this.pnlProgress.Width = new Unit(this.Step * this.blockWidth); this.pnlProgress.Visible = true; this.Processing = true; ScriptManager.RegisterStartupScript(this, this.GetType(), this.GetType().Name, string.Format(script, this.UpdatePanel1.UniqueID), true);Then in the next page load, because Processing flag is true I can keep updating the progress bar and status messages until Completed flag toggle back to true.
if (this.Processing) { if (!string.IsNullOrEmpty(this.LastStatus)) { this.pnlProgress.Width = new Unit(this.Step * this.blockWidth); this.lblMessage.Text += string.Format("<br />{0}", this.LastStatus); this.LastStatus = string.Empty; } ScriptManager.RegisterStartupScript(this, this.GetType(), this.GetType().Name, string.Format(script, this.UpdatePanel1.UniqueID), true); } else { if (!this.Completed) { lblMessage.Text += "<br />Processing is complete"; this.Completed = true; } else { this.lblMessage.Text = string.Empty; this.Step = 0; } }
Complete Example:
<%@ Page Language="C#" %> <%@ Import Namespace="System.Threading" %> <html> <head runat="server"> <script runat="server"> private string script = @"setTimeout(""__doPostBack('{0}','')"", 3000);"; protected int totalSteps = 7; private int blockWidth = 50; private string styles = @" <style type=""text/css""> .Hide {{ display:none; }} .ProgressWrapper {{ height:22px; width:{0}px; border:solid 1px #9C9C9C; padding:5px; background-color:#ddd; }} .Progress {{ height:20px; background-color:#0059FF; border:solid 1px #003EB0; float:left; }} </style> "; public bool Processing { get { return (bool)(Session["Processing"] ?? false); } set { Session["Processing"] = value; } } public int Step { get { return (int)(Session["Step"] ?? 0); } set { Session["Step"] = value; } } public string LastStatus { get { return (string)(Session["LastStatus"] ?? string.Empty); } set { Session["LastStatus"] = value; } } public bool Completed { get { return (bool)(Session["Completed"] ?? (Session["Completed"] = true)); } set { Session["Completed"] = value; } } protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (this.Processing) { if (!string.IsNullOrEmpty(this.LastStatus)) { this.pnlProgress.Width = new Unit(this.Step * this.blockWidth); this.lblMessage.Text += string.Format("<br />{0}", this.LastStatus); this.LastStatus = string.Empty; } ScriptManager.RegisterStartupScript(this, this.GetType(), this.GetType().Name, string.Format(script, this.UpdatePanel1.UniqueID), true); } else { if (!this.Completed) { lblMessage.Text += "<br />Processing is complete"; this.Completed = true; } else { this.lblMessage.Text = string.Empty; this.Step = 0; } } if (this.Page.Header != null) this.Page.Header.Controls.Add( new Literal() { Text = string.Format(this.styles, this.totalSteps * this.blockWidth + 2) }); } protected void Button1_Click(object sender, EventArgs e) { Thread thread = new Thread(new ThreadStart(SomeLongOperation)); thread.Start(); this.lblMessage.Text = "<br />Processing... Please wait..."; this.pnlProgress.Width = new Unit(this.Step * this.blockWidth); this.pnlProgress.Visible = true; this.Processing = true; ScriptManager.RegisterStartupScript(this, this.GetType(), this.GetType().Name, string.Format(script, this.UpdatePanel1.UniqueID), true); } void SomeLongOperation() { this.Completed = false; /// /// this is just to demonstrate /// should you have your own logic there /// and change the last status accordingly /// for (int i = 0; i < 25; i++) { if (i % 4 == 0) { this.Step = this.Step + 1; string status = string.Format("Step {0} - {1}", i / 4 + 1, DateTime.Now.Ticks % 2 == 0 ? "Success" : "Faild - Due to {exception details}"); if (string.IsNullOrEmpty(this.LastStatus)) this.LastStatus = status; else this.LastStatus = string.Format("{0}<br />{1}", this.LastStatus, status); } Thread.Sleep(1000); } this.Processing = false; } </script> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager runat="server" ID="pageScriptManager" /> <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional"> <ContentTemplate> <asp:Button runat="server" ID="btnDoTask" Text="Do Task" OnClick="Button1_Click" /> <hr /> <asp:Panel runat="server" ID="pnlProgressWrapper" CssClass="ProgressWrapper"> <asp:Panel runat="server" ID="pnlProgress" CssClass="Progress" Visible="false" /> </asp:Panel> <hr /> <asp:Label runat="server" ID="lblMessage" /> </ContentTemplate> </asp:UpdatePanel> </form> </body> </html>
2 comments:
Hi I have seen your great article and trying to apply it there ocurred a problem: I have a class in App_Code that does all the database work and its variables (connection, command, userdata, etc..) are stored in a Session accesor like this:
public static class SessionActual
{
public static HttpSessionState Session
{
get
{
return HttpContext.Current.Session;
}
}
public static string username
{
get
{
return Session["username"].ToString();
}
set
{
Session["username"] = value;
}
}
}
The problem is that inside a thread HttpContext.Current is null so everything fails...
How would you solve it? (Preferably without changing all the system :S)
Thanks a lot!
You can do something like this
public partial class Test : Page
{
protected void Button1_Click(object sender, EventArgs e)
{
ThreadStart operation = delegate { SomeLongOperation(HttpContext.Current); };
Thread thread = new Thread(new ThreadStart(operation));
thread.Start();
///
/// other stuff
///
}
void SomeLongOperation(HttpContext context)
{
SessionActual.SetUserName(context,"Admin");
string userName = SessionActual.GetUserName(context);
}
}
public static class SessionActual
{
///
/// Other stuff
///
public static string GetUserName(HttpContext context)
{
return (string)context.Session["username"];
}
public static void SetUserName(HttpContext context, string userName)
{
context.Session["username"] = userName;
}
}
Post a Comment