Lag in C# ProgressBar

AnomalyNexus

Senior Member
Joined
Nov 29, 2009
Messages
710
[Solved] Lag in C# ProgressBar

So I'm trying to get the hang of VS 2010 C# Express (Beta 2) and C# in general.

I've got a form with:
  • Button
  • Timer (100ms)
  • Progress Bar

Button toggles MyTimer.enabled, which has:
Code:
private void MyTimer_Tick(object sender, EventArgs e)
        {            
            MyProgressBar.Value++;          
        }

All works and I'm well chuffed.

However there is lag on the Progressbar between clicking the button and the progressbar starting/stopping. Like 400ms lag.:confused:

I don't get where the lag is coming from. Any guesses?
 
Last edited:

Saajid

Expert Member
Joined
Aug 8, 2008
Messages
4,559
If you want your progress bar to work properly, then you need to have it updated on a different thread then your current thread (on which the actual work is being done. Try using a BackgroundWorker. In almost all cases where you need to use a progress bar, the progress bar should be updated on a seperate thread. Do a Google search for "c# progress bar on background thread" or something similar. If you don't update it on a seperate thread, your worker thread will be slowed down by the amount of time it takes to redraw the progress bar, which is ALOT, since it is an I/O operation to the screen/graphics card, and I/O operations are multiple times slower the in-memory/CPU operations.

For your case above, adding an Application.DoEvents(); should do the trick. This will force the redraw of the progress bar to happen before the next timer tick happens. However, while it might appear to work well, that's not how you should implement it. Update your progress bar on a seperate thread - use an instance of the BackgroundWorker class for easier implementation.
 

AnomalyNexus

Senior Member
Joined
Nov 29, 2009
Messages
710
Thanks for the reply Toxic.

If you want your progress bar to work properly, then you need to have it updated on a different thread then your current thread (on which the actual work is being done.
Thats the thing. I'm not doing *anything* else. If I were doing calculation or something then I see how I would need threading to make the interface work.

Nothing is using CPU or render cycles, except the changing of progressbar.value every 100ms. That should leave the app idle at least 50% of the time.

For your case above, adding an Application.DoEvents(); should do the trick. This will force the redraw of the progress bar to happen before the next timer tick happens.
I've used that in another language to great effect, but its not doing anything here.

Code:
private void MyTimer_Tick(object sender, EventArgs e)
        {
            Application.DoEvents();
            MyProgressBar.Value++;          
        }

I'm starting to think that the ProgressBar code itself is broken (or the delayed reaction is intended).

If I add the following to the timer.tick:
Code:
Testlabel.Text = Testlabel.Text + ".";
That shows no lag. Reacts instantly to the toggling (well 100ms instantly). So the timer and the overall app rendering is not the problem.

Weird.:wtf:
 

Saajid

Expert Member
Joined
Aug 8, 2008
Messages
4,559
Put Application.DoEvents(); after MyProgressBar.Value++;

If that doesn't work, then it definitely is weird...

Maybe a bug in c# or VS? It is a beta after all...
 

Necuno

Court Jester
Joined
Sep 27, 2005
Messages
58,567
the form is only going to repaint and show to be completed after the incrementation of the progress bar is completed as this is happening in the same event on the same thread. hence you must do it on a different thread than the thread running the form.
 

dequadin

Expert Member
Joined
May 9, 2008
Messages
1,434
Another issue could be the difference between the Debug build and the Release build? Which build are you working with? Try the release build and see if that helps?

I downloaded you code and ran it through .NET reflector. I had a look and there's nothing that should be slowing down your code? Your ProgressBar.Style = Blocks, maybe change that to ProgressBar.Style = Continuous it could be giving the illusion of being delayed because a certain number of steps would have to take place before the first block is drawn.

I added some stuff to your code.
  • Using a System.Timers.Timer (Gives better "tick" resolution than a Forms.Timer
  • Background worker example

You can download my code here

Maybe get yourself this free profiler: EQATEC Profiler, it'll help you discover any bottlenecks in your code.
 

AnomalyNexus

Senior Member
Joined
Nov 29, 2009
Messages
710
[Solved]

There really is animation lag in the progress bar. i.e. If the progress bar hits 100/100, the animation is still 600ms away from finished. Same for starting the progress bar. The visual representation lags the values by 600ms.:(

Thanks dequadin. Figured it out while looking at your code. Loads of interesting stuff there too RE threading.:)

Some googling reveals that this is a side effect of Vista trying to smooth the animation out. Lag is a side effect. Only workaround I can find is to edit the max property instead of value.:wtf:

Thanks all


Code:
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;

namespace A03_Forms
{
    public partial class MyForm : Form
    {
        public MyForm()
        {
            InitializeComponent();
        }

        private void MyButton_Click(object sender, EventArgs e)
        {           
           MyTimer.Enabled = !(MyTimer.Enabled);
           if (MyTimer.Enabled)
           {
               MyLabel.Text = "Timer is on";
           }
           else
           {
               MyLabel.Text = "Timer is off";
           }
        }

        private void MyTimer_Tick(object sender, EventArgs e)
        {
            Application.DoEvents();
            MyProgressBar.Value++;            
            Testlabel.Text = Testlabel.Text + "."; //As visual comparison
        }

        private void OtherButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show("The button has been clicked!", "Hello");
        }        
    }
}

And the zipped solution directory. [Quickshare.co.za 40KB]

And an animated gif of the problem [imgur.com 100KB]

Put Application.DoEvents(); after MyProgressBar.Value++;

If that doesn't work, then it definitely is weird...
No difference in where I put it. Also tried MyProgressbar.refresh() and MyProgressbar.update().

the form is only going to repaint and show to be completed after the incrementation of the progress bar is completed as this is happening in the same event on the same thread. hence you must do it on a different thread than the thread running the form.
Yeah, I get that. But I refuse to accept that I need to thread the hell out of an app just to get a progressbar to work properly. Wasn't necessary in any other Lang/IDE so far.

Another issue could be the difference between the Debug build and the Release build? Which build are you working with? Try the release build and see if that helps?
Release I think. The exe is in a folder called release.

Your ProgressBar.Style = Blocks, maybe change that to ProgressBar.Style = Continuous it could be giving the illusion of being delayed because a certain number of steps would have to take place before the first block is drawn.
No difference.
 

Necuno

Court Jester
Joined
Sep 27, 2005
Messages
58,567
Yeah, I get that. But I refuse to accept that I need to thread the hell out of an app just to get a progressbar to work properly. Wasn't necessary in any other Lang/IDE so far.

lol "thread the hell out of an app" seriously it's just a secondary thread. for example when you do batch processing on time sheets and have to display a progress bar, you are going to use threads.

here is something very basic. you might run into "cross thread operation not valid" and this is how to solve it. ( yep it's ancient something i looked at in 2006 :D )

Code:
        //method to control the control
        private void SwitchLabel()
        {
            if(this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(SwitchLabel));
            }
            else
            {
                label1.Text = "switched";
            }

        }

        //thread method
        private void trDo()
        {
            /*

            do thread work here

            */

            SwitchLabel();
        }


        private void button1_Click(object sender, EventArgs e)
        {
            Thread tr = new Thread(trDo);
            tr.Start();
        }
 

Keeper

Honorary Master
Joined
Mar 29, 2008
Messages
23,624
Yeah, I get that. But I refuse to accept that I need to thread the hell out of an app just to get a progressbar to work properly. Wasn't necessary in any other Lang/IDE so far.

yeah - I mean, it's just a progress bar ffs...
 

dequadin

Expert Member
Joined
May 9, 2008
Messages
1,434
[Solved]

There really is animation lag in the progress bar. i.e. If the progress bar hits 100/100, the animation is still 600ms away from finished. Same for starting the progress bar. The visual representation lags the values by 600ms.:(

Thanks dequadin. Figured it out while looking at your code. Loads of interesting stuff there too RE threading.:)

Some googling reveals that this is a side effect of Vista trying to smooth the animation out. Lag is a side effect. Only workaround I can find is to edit the max property instead of value.:wtf:

Indeed this seems to be a Visa/Windows 7 issue. I found this post on StackOverflow that details a similar problem. (there are a few workarounds there)

Nice find!
 

Keeper

Honorary Master
Joined
Mar 29, 2008
Messages
23,624
I code my own progress bars anyhow - i'm sure you could do the same?
 
Top