Robin
Robin

Reputation: 1967

Progressbar not showing the correct values while Multi-Threading

On my MainForm I start a thread which does some SQL-related work. In the meantime I want a new form to show with a Progressbar to confirm that the program is still responding. The Problem I'm having is that the Progressbar doesn't show the correct values. I want the Progressbar to go from Minimum to Maximum over and over, just to ensure the user that the program isn't stuck. However the Progressbar doesn't appear the way I want it to, because It resets when It's reached 2/3 of it's Maximum value. So the Progressbar is showing values from Minimum to 2/3 of Maximum. Below is a picture of when the Progressbar resets to Value = 0;

enter image description here

The MainForm contains the following code:

List<Table> ContentList = new List<Table>();
using (ConnectingForm CF = new ConnectingForm())
{
    CF.StartPosition = FormStartPosition.Manual;
    CF.Show(this);
    Thread thread = new Thread(() => { ContentList = DBL.LoadSQLData(); });
    thread.Start();
    DBL.SQLdone = false;
    while (!DBL.SQLdone);
    this.Focus();
    CF.Hide();
}

And the ConnectingForm has the following code:

public ConnectingForm()
{
    InitializeComponent();
    progressBar1.Value = 0;
    progressBar1.Minimum = 0;
    progressBar1.Maximum = 50;
    progressBar1.Step = 1;
    timer1.Interval = 25;
    timer1.Enabled = true;
}

private void timer1_Tick(object sender, EventArgs e)
{
    if (progressBar1.Value < progressBar1.Maximum)
        progressBar1.PerformStep();
    else
        progressBar1.Value = 0;
}

I have also tried to set the Progressbar's value inside the while loop. However the problem still exists: the UI doesn't show the animation of the last 1/3 of the Progressbar Value increase. At the moment the code looks like this:

MainForm:

...
Thread thread = new Thread(() => { ContentList = DBL.LoadSQLData(); });
thread.Start();
DBL.SQLdone = false;
while (!DBL.SQLdone)
{
    if (CF.Value == CF.Maximum)
         CF.Value = 0;
    else
         CF.Value += 1;
    Thread.Sleep(100);
}
...

While the ConnectingForm looks like this:

public ConnectingForm()
{
    InitializeComponent();
    progressBar1.Value = 0;
    progressBar1.Minimum = 0;
    progressBar1.Maximum = 20;
    progressBar1.Step = 1;
}
public int Value
{
    set { progressBar1.Value = value; }
    get { return progressBar1.Value; }
}
public int Maximum
{
    get { return progressBar1.Maximum; }
}

Which still gives the same results. The Progressbar only show the Value 0 to 2/3, and then it is reset.

I'm hoping that someone can help me figure out what I'm doing wrong. Thanks in advance!

Upvotes: 1

Views: 5261

Answers (4)

King King
King King

Reputation: 63317

I think the ProgressBar is designed to indicate a little slow progress, such fast or rapid progress (which changes 1/50 in 25 miliseconds) can't be updated correctly (in time). While the ProgressBar.Value is still increased and reaches the maximum value to restart the loop. At that time, the ProgressBar visual indicator just reaches at 2/3 of the maximum value (delayed compared with the actual Value), but somehow it updates the visual indicator right when the value 0 is set and the 1/3 remaining visual indicator is not updated/drawn/rendered. If you increase the Interval of your timer such as to 1000, you can see it run through all the bar before restarting from beginning. I think it's by design and can't change the way it works. You have some options here (I can think of):

1. Try creating your own progress indicator and make it update the visual indicator the way you want, however I think it may cause some small issue related to performance.
2. Try cutting off the `non-updated visual part of your ProgressBar` using `Region` property like this:

    //First, try drawing the length of your ProgressBar more because we have to cut off the remaining non-updated visual part of it.
    yourProgressBar.Region = new Region(new Rectangle(Point.Empty, new Size(2*yourProgressBar.Width/3, yourProgressBar.Height)));//placing this in your Form constructor is OK.

Maybe you have to test if it runs through all the length of your progressbar and correct the Width of the Rectangle passed in the Region accordingly.

UPDATE

In fact ProgressBar supports working progress indicator (without caring about percentage), I think you may want to use it without creating any other control or using a third-party control. All you need is here:

yourProgressBar.Style = ProgressBarStyle.Marquee;
yourProgressBar.MarqueeAnimationSpeed = 1;//Change the speed of marquee animation
//You don't need any timer to change its Value at all.

Upvotes: 3

keenthinker
keenthinker

Reputation: 7820

First things first: normally you change the value of the progress bar not in a timer, but on every unit of work (a step) from your process, so in your case the progress bar should change in the while loop!

From your code is not possible to see when the timer is started and stopped! It looks like you add more steps to your progress bar as the defined maximum value! You change the value of the progress bar on every 25 milliseconds and this value has no connection at all with your SQL query load/read/etc.

So you need to change the flow and the logic of the program in order to set the correct value. You also need to be aware of cross thread access to UI elements if you want to update the progress bar from the thread.

Upvotes: 0

JeffRSon
JeffRSon

Reputation: 11166

This

Application.DoEvents();
Thread.Sleep(10);

is evil. Don't do that. And, besides, you don't need it. I would assume it'll work without it.

Upvotes: 3

Maurizio In denmark
Maurizio In denmark

Reputation: 4284

Can you try changin this line:

if (progressBar1.Value < progressBar1.Maximum)

with this one:

if (progressBar1.Value <= progressBar1.Maximum)

Upvotes: 0

Related Questions