locked
Progressbar draws too slowly RRS feed

  • Question

  • I have written a little dialog class that I can pop up during long processes and set the Title, message, and progress bar values.  This dialog automatically calculates the percentage of completion and displays it in a label under the progress bar. 

    My problem is that most of my processes increment the value in the progress dialog quicker than the progressbar on the dialog can draw and display the changes.  This ends up causing the label with my calculated percentage to reach 100% WAY before the progress bar show 100%. The progress bar in some cases looks like it is at 50% or less.

    Is there a way to syncronize the progressbar by sleeping my thread until the progress bar is done updating?  Below is a copy of the progress bar dialog Layout and Code.

        partial class frmProgress
        {
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.IContainer components = null;
    
            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Windows Form Designer generated code
    
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                this.pbar = new System.Windows.Forms.ProgressBar();
                this.lblPercent = new System.Windows.Forms.Label();
                this.lblMessage = new System.Windows.Forms.Label();
                this.SuspendLayout();
                // 
                // pbar
                // 
                this.pbar.Location = new System.Drawing.Point(12, 39);
                this.pbar.Name = "pbar";
                this.pbar.Size = new System.Drawing.Size(260, 23);
                this.pbar.Step = 1;
                this.pbar.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
                this.pbar.TabIndex = 0;            
                // 
                // lblPercent
                // 
                this.lblPercent.Location = new System.Drawing.Point(12, 65);
                this.lblPercent.Name = "lblPercent";
                this.lblPercent.Size = new System.Drawing.Size(260, 23);
                this.lblPercent.TabIndex = 1;
                this.lblPercent.Text = "0%";
                this.lblPercent.TextAlign = System.Drawing.ContentAlignment.TopCenter;
                // 
                // lblMessage
                // 
                this.lblMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
                this.lblMessage.ForeColor = System.Drawing.Color.Navy;
                this.lblMessage.Location = new System.Drawing.Point(12, 9);
                this.lblMessage.Name = "lblMessage";
                this.lblMessage.Size = new System.Drawing.Size(260, 27);
                this.lblMessage.TabIndex = 2;
                this.lblMessage.Text = "Message";
                this.lblMessage.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
                // 
                // frmProgress
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(284, 96);
                this.ControlBox = false;
                this.Controls.Add(this.lblMessage);
                this.Controls.Add(this.lblPercent);
                this.Controls.Add(this.pbar);
                this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
                this.MaximizeBox = false;
                this.MinimizeBox = false;
                this.Name = "frmProgress";
                this.Text = "Please Wait...";
                this.Load += new System.EventHandler(this.frmProgress_Load);
                this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.frmProgress_FormClosed);
                this.ResumeLayout(false);
    
            }
    
            #endregion
    
            private System.Windows.Forms.ProgressBar pbar;
            private System.Windows.Forms.Label lblPercent;
            private System.Windows.Forms.Label lblMessage;
        }
    
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
        public partial class frmProgress : Form
        {
            public void Increment(int Value)
            {
                pbar.Increment(Value);
                Recalc();
            }
            public void PerformStep()
            {
                pbar.PerformStep();
                Recalc();
            }
    
    
            public int Total
            {
                get
                {
                    return pbar.Maximum;
                }
    
                set
                {
                    
                    pbar.Maximum = value;
                    pbar.Update();
                    Application.DoEvents();
                    Recalc();                
                }
            }
    
            public string ProgressMessage
            {
                get
                {
                    return lblMessage.Text;
                }
    
                
                set
                {
                    lblMessage.Text = value;
                }
                
            }
    
            public int Value
            {
                get
                {
                    return pbar.Value;
                }
    
                set
                {
                    pbar.Value = value;                
                    Recalc();
                }
    
            }
            public frmProgress()
            {
                InitializeComponent();
            }
    
            private void frmProgress_Load(object sender, EventArgs e)
            {
    
                Cursor.Current = Cursors.WaitCursor;
            }
    
            private void Recalc()
            {
                if (pbar.Maximum == 0)
                {
                    lblPercent.Text = "0%";
                }
                else
                {
    
                    lblPercent.Text = ((float)pbar.Value / (float)pbar.Maximum).ToString("0.#%");                
                }
                Refresh();
            }
    
            private void frmProgress_FormClosed(object sender, FormClosedEventArgs e)
            {
                Cursor.Current = Cursors.Default;
            }
    
    
    
    
        }
    



    Thanks, John Hamilton POSitive Software Company http://www.gopositive.com
    • Moved by Rudedog2 Wednesday, February 3, 2010 7:59 PM winforms issue (From:Visual C# General)
    Wednesday, February 3, 2010 7:40 PM

All replies

  • Take a look at my Form in this thread, while I take a look at yours.

    http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/52dcb460-90a3-45f2-ae23-bcb60514c542

    It has the same form in VB, C#, and C++.

    Rudy  =8^D

    Mark the best replies as answers. "Fooling computers since 1971."
    Wednesday, February 3, 2010 7:54 PM
  • Get rid of DoEvents, Refresh, Update, and ReCalc. They are bogging down your Progress Bar.
    Avoid Sears Home Improvement
    Wednesday, February 3, 2010 8:06 PM
  • Hi John,

    I have made a test which can reproduce the issue. Here is what I have done.
    private void UpdateLableAndProgressBar(Label label, ProgressBar progressBar, int incrementValue)
            {
                progressBar.Increment(incrementValue);
                label.Text = ((float)progressBar.Value / (float)progressBar.Maximum).ToString("0.#%");
                Refresh();
            }

    private void btnUpdateUI_Click(object sender, EventArgs e)
            {
                for (int i = 0; i < 100; i += 10)
                {
                    UpdateLableAndProgressBar(label1, progressBar1, 10);
                    System.Threading.Thread.Sleep(1000);
                }
            }
    The code is very simple. When the UpdateLableAndProgressBar method is called, the Label and the ProgressBar will be updated by 10. After I click the btnUpdateUI, the ProgressBar and the label will be updated by 10 per second. I noticed the progressbar was a little delay. This test was made on Win7. Each time I set the Value of the ProgressBar, it doesn’t immediately turn to new value. Instead, it animates the progress. I think that cause the delay draw.

    I copy the code to a Win2003 computer and run it, the ProgressBar and the Label shows the value synchronously. Is it the problem you are facing now?

    Sincerely,
    Kira Qian
    MSDN Subscriber Support in Forum
    If you have any feedback on our support, please contact msdnmg@microsoft.com
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework!
    Friday, February 5, 2010 8:16 AM
  • Yes this is EXACTLY the problem.  Good to know it seems to be a Windows 7 thing.  If only there was an event when the progress bar finished animating, I could sleep the thread and hold off my recalc of the label until the event fired or something.  However there is no such event.

    Does anyone have an idea on this?  I haven't tried the background worker thing mentioned above yet, but I don't think it will help.


    Thanks, John Hamilton POSitive Software Company http://www.gopositive.com
    Friday, February 5, 2010 2:49 PM
  • Here's a working example using the BackgroundWorker:

    Label lblStatus = new Label();
    ProgressBar pb1 = new ProgressBar();

    void testOnly() {
      const int MAX_LOOPS = 100;
      using (BackgroundWorker worker = new BackgroundWorker()) {
        worker.WorkerReportsProgress = true;
        worker.WorkerSupportsCancellation = true;
        worker.DoWork += delegate(object sender, DoWorkEventArgs e) {
          for (int i = 0; (i < MAX_LOOPS) && (!worker.CancellationPending); i++) {
            int count = i + 1;
            worker.ReportProgress(count, string.Format("{0} of {1}", count, MAX_LOOPS));
          }
        };
        worker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e) {
          pb1.Value = e.ProgressPercentage;
          lblStatus.Text = e.UserState.ToString();
        };
        worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
          Cursor = Cursors.Default;
          pb1.Visible = false;
        };
        worker.RunWorkerAsync();
        if (worker.IsBusy) {
          pb1.Value = 0;
          pb1.Maximum = MAX_LOOPS;
          Cursor = Cursors.AppStarting;
        }
      }
    }


    Avoid Sears Home Improvement
    Friday, February 5, 2010 3:35 PM
  • Get rid of DoEvents, Refresh, Update, and ReCalc. They are bogging down your Progress Bar.
    Avoid Sears Home Improvement



    Good advice.  Get rid of DoEvents. 
    Although, I don't see where it ever gets executed in the posted code.

    And, a BGW is one solution to your problem.  Don't know if it will cure Windows 7, though.

    Mark the best replies as answers. "Fooling computers since 1971."
    • Edited by Rudedog2 Friday, February 5, 2010 5:04 PM spelling
    Friday, February 5, 2010 4:13 PM
  • DoEvents is not the problem.  I put DoEvents in there as a way to see if I could get the thread to finish drawing the taskbar (in a blocking way) before continuing execution.

    Performance is not a problem here, it is aesthetics.  If the process is iterating faster than the progress bar can "animate" then I would like to find a way to block the iteration while the animation finishes.  This will produce a progress bar and label that are accurate.

    Any other suggestions?

     
    Thanks, John Hamilton POSitive Software Company http://www.gopositive.com
    Friday, February 5, 2010 4:17 PM
  • Thread?  Updating controls from a separate thread. 
    Windows 7.  What else is going on?

    We never said DoEvents was the cause of the issue. 
    In fact, I also pointed out that it NEVER gets executed in the code that you posted.

    You were simply advised to get rid of it, and stop using it.
    DoEvents is a hack that was included in the Framework to ease the transition into .NET for VB6 users.

    It is also prone to giving you unexpected results.  All of this is well-documented.
    The need and desire to use it, is a sign of less than the best program design, too.  There are better ways.

    Rudy  =8^D

    Mark the best replies as answers. "Fooling computers since 1971."
    Friday, February 5, 2010 4:24 PM
  • Yeah I will be eliminating it.  This particular program is not multi-threaded.  What I am saying is that if I had a way to sleep the execution thread while the animation is finishing then I think it would be more accurate.

    This is evident by the following code:
                frmProgress Prog;
    
                Prog = new frmProgress();
                Prog.Value = 0;
                Prog.ProgressMessage = "Testing...";
                Prog.Total = 204;            
                Prog.Show(this);
    
                for (int i = 0; i < Prog.Total; i++)
                {
                    Prog.PerformStep();
                    //System.Threading.Thread.Sleep(50);
                }
    If I enable the commented Sleep and tweak with the time on it, the progress animation becomes more and more accurate.  However this is a hack way of fixing the problem.  I would rather use a synchronization object so I have the most accurate amount of time for my thread to sleep.

    I realize that sleeping the execution thread would seem to be self-defeating but the progress bar must be drawing on its own thread or something because it continues to animate when the execution thread is sleeping.



    Thanks, John Hamilton POSitive Software Company http://www.gopositive.com
    Friday, February 5, 2010 4:41 PM
  • I don't have Windows 7.  Does my sample show the same behavior on Windows 7?

    Mark the best replies as answers. "Fooling computers since 1971."
    Friday, February 5, 2010 5:02 PM
  • Rudedog2: I can't navigate to your example. IE8 says "Forbidden" and Google Chrome just shows a blank page. Perhaps the link is bad, or it is somehow tied to your account.

    John, I created a version based on yours that uses a static form. Just about identical and still has the Win7 "slow poke" progress bar - but it is thread safe!

    If you would like to see it, I can email you a copy - I just can't seem to upload to *any* site from behind our company's proxy. Email me from this link: http://www.joeswelding.biz/contact

    ~Joe
    Avoid Sears Home Improvement
    Friday, February 5, 2010 5:32 PM
  • Take that back: I uploaded it to my Google Site:

    http://sites.google.com/site/jp2code/home/list/ProgressBar1.zip

    Quite a mouthful, but you don't have to share your email address with anyone that way!
    Avoid Sears Home Improvement
    Friday, February 5, 2010 5:34 PM
  • I just tried it in a different browser.

    http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/52dcb460-90a3-45f2-ae23-bcb60514c542

    http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/52dcb460-90a3-45f2-ae23-bcb60514c542

    "Forbidden" suggests that some web filter is in place.  That is not an IE message.
    The link is to a different thread in this same forum.

    Rudy  =8^D

    Mark the best replies as answers. "Fooling computers since 1971."
    Friday, February 5, 2010 5:36 PM
  • Rudy,

    You version does it too (in Win7): When you click "Pause", the progress bar continues until it catches up until the actual value. Kind of funny looking.

    One really annoying part is that the progress bar always finishes *before* the form actually displays it being finished. (Numerical values, Progress Bar Value, it doesn't seem to matter). It's almost like Microdummies designed the form to lag behind reality by about 2 seconds.

    This would really suck if you had something that relied on critical timing. I wonder why Microsoft did this?
    Avoid Sears Home Improvement
    Friday, February 5, 2010 6:05 PM
  • Thanks.  I didn't know that.

    Mark the best replies as answers. "Fooling computers since 1971."
    Friday, February 5, 2010 6:33 PM
  • I can't say that we've answered this.  As jp2code pointed out, everything about the progressbar says it should be at a certain percent visually (the Min, the Max, the Value), however when you have a quick process, it will easily have values that reflect 100% but the visual indication will be 50% or less and it will take like 2 to 5 seconds to catch up.

    What they need to do is do an event that fires when drawing is complete or they need to allow the progress bar to block execution until the drawing is complete whenever values are updated.

    So something like this:

    Update a value by setting a property or executing step.
    Do not allow the execution thread to continue until drawing is completed.

    Like I said before, performance be damned in these cases.  I want my program to show a semi-accurate visual representation of the percentage.  I don't care if the progress update slows my program by 100ms per iteration.  I just want it to work and look good.

    Anyway.  Enough ranting.

    It sounds like there is no answer to this problem.  So what should I do as far as solving it (3rd Party Progress) and marking an answer.


    Thanks, John Hamilton POSitive Software Company http://www.gopositive.com
    Friday, February 5, 2010 6:41 PM
  • Oh and I avoid Sears all together now!  Their service and sense of duty to their customers is non-existent.  I will buy their products second hand but I will never give sears another cent.


    Thanks, John Hamilton POSitive Software Company http://www.gopositive.com
    Friday, February 5, 2010 6:43 PM
  • Uh, exactly the same problem I have on Windows 7. Any1 found solution for this?
    Wish you the best!
    Saturday, February 26, 2011 12:45 AM
  • In you Value property, try setting the pbar.Value like this:

    pbar.Value = value;

    pbar.Value = value - 1;

    pbar.Value = value;

     

    An ugly hack to get around the problem.

    Chris

    • Proposed as answer by Rudedog2 Saturday, February 26, 2011 1:18 PM
    Saturday, February 26, 2011 2:27 AM
  • In your Value property, try setting the pbar.Value like this:

    pbar.Value = value;

    pbar.Value = value - 1;

    pbar.Value = value;

    It is caused by animations associated with your desktop settings.  It animates the PB as it increases, but not while it decreases.  And that is why the above hack sppears to 'fix' the problem.  The hack decreases the PB.Value, which causes an immediate update without animation. 

    My sample uses pb.PerformStep, which is the preferred way to update the Value property.  I suppose you could alter the Step property to achieve the same result, causing the Value to decrease to fool the desktop animations.


    Mark the best replies as answers. "Fooling computers since 1971."

    http://rudedog2.spaces.live.com/default.aspx

    Saturday, February 26, 2011 1:50 PM