Web Progress Bar in c#

10,585

Solution 1

I used the Idea of @Mike.. and altered it a bit because the HttpContext.Current is null... and replaced it with an static object. this worked good for me also made some concurrency test and worked fine ..

<div style="width: 800px" class="css_container">
        <cc1:ToolkitScriptManager ID="sm" runat="server" EnableScriptGlobalization="true"
            EnableScriptLocalization="true" AsyncPostBackTimeout="60000" EnablePageMethods="true">
        </cc1:ToolkitScriptManager>
        <asp:UpdatePanel ID="upPanel" runat="server">
            <ContentTemplate>
               <asp:HiddenField ID="correlationKey" runat="server" />
               <asp:Button ID="btn" runat="server"  Text="Do Something"
                                    CausesValidation="False" OnClick="btn_Click" OnClientClick="javascript:GetProgress();" />
            </ContentTemplate>
        </asp:UpdatePanel>
  </div>

function GetProgress() {
    PageMethods.GetProcessed(document.getElementById("correlationKey").value,
        function(result) {
            alert(result);
            if (result < 100) {
                setTimeout(function(){GetProgress();}, 1000);
            }   
        });   
}

protected void btn_Click(object sender, EventArgs e) 
{
    // NEW CODE
    // set the hidden field value here with a correlation key
    var correlationKey = Guid.NewGuid().ToString();
    this.correlationKey.Value = correlationKey;

    AClass oClass = new AClass(); 
    Thread oThread = new Thread(delegate() 
        { 
            oClass.UpdateSession(correlationKey); 
        }); 
    oThread.Start(); 
}

public class AClass
{
    public static Dictionary<string, int> ProcessedQueue { get; set; }

    public void UpdateSession(string correlationKey)
    {
        var dictionary = ProcessedQueue; // HttpContext.Current.Application["ProcessedQueue"] as Dictionary<string, int>;
        if (dictionary == null)
        {
           dictionary = new Dictionary<string, int>();
           ProcessedQueue = dictionary;// HttpContext.Current.Application["ProcessedQueue"] = dictionary;
        }

        if (!dictionary.ContainsKey(correlationKey)) { dictionary.Add(correlationKey, 0); }

        for (int i = 0; i < 100; i++)
        {
             dictionary[correlationKey] = i;
         System.Threading.Thread.Sleep(1000);
        }
    }
}

[WebMethod(EnableSession = true), ScriptMethod]
public static string GetProcessed(string correlationKey)
{
     var dictionary = AClass.ProcessedQueue; //     HttpContext.Current.Application["ProcessedQueue"] as Dictionary<string, int>;
     if (dictionary == null || !dictionary.ContainsKey(correlationKey)) { return "0"; }
     return dictionary[correlationKey].ToString();
}

Solution 2

I think there might be a few things going on here:

  1. Your thread might be terminating at the end of the response. I think you can fix that by changing the way you define your thread:

    Thread oThread = new Thread(new ThreadStart(oClass.UpdateSession));
    oThread.IsBackground = true; // Set this
    oThread.Start();
    
  2. You're returning a string percent to your callback function, and then comparing it against a number. It's not really an issue, but for the sake of good programming, you should change your server-side GetProcessed() to return an actual integer:

    public static int GetProcessed()
    {
        int result = 0;
        int.TryParse(HttpContext.Current.Session["processed"].ToString()), out result);
        return result;
    }
    
  3. UpdatePanel requests initiate a full page lifecycle behind the scenes. Can you verify that this code isn't resetting your progress every tick of your GetProcessed() web method?

    if (!IsPostBack)
    {
         Session["processed"] = 0;
    }
    

Solution 3

The issue is that you are calling in on a different Session than the one the value is being updated on. So, you have a couple of options.

First you could make that an Application variable if your application is only used by one user (very unlikely but I don't know anything about the app).

Second, you can use some sort of correlation key so that you can get the value from an Application variable that is keyed.

Make a change in the button click:

protected void btn_Click(object sender, EventArgs e) 
{
    // NEW CODE
    // set the hidden field value here with a correlation key
    var correlationKey = Guid.NewGuid().ToString();
    this.correlationKey.Value = correlationKey;

    AClass oClass = new AClass(); 
    Thread oThread = new Thread(delegate() 
        { 
            oClass.UpdateSession(correlationKey); 
        }); 
    oThread.Start(); 
}

Now modify the JavaScript like this:

function GetProgress() {
    PageMethods.GetProcessed(document.getElementById("correlationKey").value,
        function(result) {
            alert(result);
            if (result < 100) {
                setTimeout(function(){GetProgress();}, 1000);
            }   
        });   
}

Now modify the GetProcessed method like this:

[WebMethod(EnableSession=true), ScriptMethod]
public  static string GetProcessed(string correlationKey)
{
    var dictionary = Application["ProcessedQueue"] as Dictionary<string, int>;
    if (dictionary == null || !dictionary.ContainsKey(correlationKey)) { return "0"; }
    return dictionary[correlationKey].ToString();
}

Now modify the UpdateSession method like this:

public void UpdateSession(string correlationKey)
{
    var dictionary = Application["ProcessedQueue"] as Dictionary<string, int>;
    if (dictionary == null)
    {
        dictionary = new Dictionary<string, int>();
        Application["ProcessedQueue"] = dictionary;
    }

    if (!dictionary.ContainsKey(correlationKey)) { dictionary.Add(correlationKey, 0); }

    for (int i = 0; i< 100; i++)
    {
        dictionary[correlationKey] =  i;
        System.Threading.Thread.Sleep(1000);
    }
}

And now clean out the Page_Load.

Solution 4

Maybe I missed something, but your javascript (at least what you put in the question), isn't updating anything on the page. You said you have 0 in the progress bar all the time, which could be because you commented out the line that changes the display.

//document.getElementById("divProgress").innerHTML = result + "%";

Is the alert showing the correct value? Also, have you confirmed that UpdateSession() is running correctly and actually setting a value in the session?

Share:
10,585

Related videos on Youtube

Hector Sanchez
Author by

Hector Sanchez

Updated on June 04, 2022

Comments

  • Hector Sanchez
    Hector Sanchez almost 2 years

    I'm looking for a way to display a "progress bar" in web using webforms with c#.

    I made a quick example, I just want a label to show like 1%, 2%, 3% etc.. about a background process that will be running in the server. And when it finish update something like showing an alert or something, The real need is a little more complicated than that but getting the bases I think I can make it by my own. The problem that I having with the code is that I'm always getting 0.. in my "progress bar" it doesn't get updated, I'm missing something but I don't know what it is.

    Edit

    I tried to alert the value every second to see the value instead of the label, but it is not working anyway.

    I missed the OnClientClick="javascript:GetProgress();" in the first queston, i Updated it, it is not working anyway

    Edit 2

    HttpConext.Current is being null when calling it as a thread.. Should I use something more than a session or application, perhaps a singletone class?

    Any Help would be really appreciated.


    Importanth thigs from The ASPX and JS

     <div style="width: 800px" class="css_container">
            <cc1:ToolkitScriptManager ID="sm" runat="server" EnableScriptGlobalization="true"
                EnableScriptLocalization="true" AsyncPostBackTimeout="60000" EnablePageMethods="true">
            </cc1:ToolkitScriptManager>
            <asp:UpdatePanel ID="upPanel" runat="server">
                <ContentTemplate>
                   <asp:Button ID="btn" runat="server"  Text="Do Something"
                                        CausesValidation="False" OnClick="btn_Click" OnClientClick="javascript:GetProgress();" />
                </ContentTemplate>
            </asp:UpdatePanel>
      </div>
    

      function GetProgress() {
           PageMethods.GetProcessed(function(result) {
                    alert(result);
                    if (result < 100) {
                        setTimeout(function(){GetProgress();}, 1000);
                    }                  
                });
    
        }
    

    Important Things from Code Behind

    [WebMethod(EnableSession=true), ScriptMethod]
        public  static string GetProcessed()
        {
            return HttpContext.Current.Session["processed"].ToString();
        }
    
    protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                 Session["processed"] = 0;
            }
        }
    
    protected void btn_Click(object sender, EventArgs e)
        {
           AClass oClass = new AClass();
            Thread oThread = new Thread(delegate()
                {
                    oClass.UpdateSession();
                });
               oThread.Start();
        }
    

    Important Things from the Aclass

    public class AClass
    { 
        public void UpdateSession()
        {
              for(int i = 0; i< 100; i++)
              {
    
                HttpContext.Current.Session["processed"] =  i;
                System.Threading.Thread.Sleep(1000);
              }
        }
    }
    
    • Mike Perrenoud
      Mike Perrenoud over 11 years
      Would it be because this line //document.getElementById("divProgress").innerHTML = result + "%"; is commented out?
  • Gromer
    Gromer over 11 years
    Can you verify UpdateSession() is running?
  • Gromer
    Gromer over 11 years
    Also, is your GetProgress() in your javascript running off the timer as it should be since the value is < 100? I'm assuming that you're getting an alert every second with 0 in it?
  • Hector Sanchez
    Hector Sanchez over 11 years
    right, the alert shows itself every second with the value of 0