1

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!

Community
  • 1
  • 1
Robin
  • 1,927
  • 3
  • 18
  • 27

4 Answers4

3

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.

JeffRSon
  • 10,404
  • 4
  • 26
  • 51
  • not really, using `Application.DoEvents()` properly will be OK. Of course test it first to find any issue with it before deploying the application. At least, I don't know any other method to replace `Application.DoEvents()` – King King Jul 09 '13 at 09:08
  • But how will the UI work if the MainThread is stuck on the line `while (!DBL.SQLdone);`? – Robin Jul 09 '13 at 09:11
  • @KingKing You cannot know if you use it "properly". There are many possible side effects. It's often used as "poor man's multithreading", but mostly it makes things worse than the benefit it has. It should be avoided at all. And that's not only my personal opinion. There are numerous other references. – JeffRSon Jul 09 '13 at 09:13
  • @Robin: You should never block your main thread. That's why you use thread, don't you. – JeffRSon Jul 09 '13 at 09:13
  • I'm just thinking about how we make a pause in the code until the thread is done working. Will the Thread.Join() procedure allow the UI to update? – Robin Jul 09 '13 at 09:17
  • @Robin JeffRSon suggested you to remove the code above and it should work as what you want, now please try and let us know the result? – King King Jul 09 '13 at 09:17
  • Why would you want to pause the code? If you need to do something at the end of the thread then either call it from the thread (using (Begin)Invoke) or use the `BackgroundWorker` class which may be simpler for beginners. – JeffRSon Jul 09 '13 at 09:20
  • It's true that the `Application.DoEvents(); Thread.Sleep(10);` isn't required. Sorry for doubting you. However the Progressbar still is restored on 2/3 of it's maximum value... – Robin Jul 09 '13 at 09:26
3

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.
King King
  • 61,710
  • 16
  • 105
  • 130
  • The second solution would probably work, but I rather not use a 'hack'. I might consider creating my own object and dispose of the Windows Progressbar... Thanks for taking the time to explain why it doesn't work :) – Robin Jul 09 '13 at 10:07
  • 1
    @Robin if so, you should go for the first solution, I think indicating that there is being some work doesn't require rapid progress, you can create your own indicator easily with some basic knowledge of GDI+ or you may want to use some third party control, DotNetBar supports some indicators which looks great. – King King Jul 09 '13 at 10:10
  • Update: That is absolutely brilliant! This solves all my problems. Thank you very much, sir! – Robin Jul 09 '13 at 10:31
0

Can you try changin this line:

if (progressBar1.Value < progressBar1.Maximum)

with this one:

if (progressBar1.Value <= progressBar1.Maximum)
Maurizio In denmark
  • 4,226
  • 2
  • 30
  • 64
  • That actually makes the Progressbar go all the way to the end, but the program will never reach the else-line.. So I'm probably gonna have to add a global bool or something to determine if it should be reset. I will try this and get back to you with the result. Cheers! – Robin Jul 09 '13 at 09:12
  • The above works due to the fact that the program increase the Value until it reaches it's Maximum Value, and then the program stops. However all ways of resetting the Value will result in that the UI can't keep up and some of the visualization will be lost. – Robin Jul 09 '13 at 09:44
0

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.

keenthinker
  • 7,645
  • 2
  • 35
  • 45
  • Okay thanks for that tip. I tried changing the Progressbar Value inside the while loop. But the problem still occurs where the UI doesn't show 1/3 of the animations. – Robin Jul 09 '13 at 09:45
  • Progress bar maximum value should be count of results of your query - all iterations. – keenthinker Jul 09 '13 at 09:59
  • Well my goal is not to show how many percent of the progress that are completed. What I want to do is to ensure the user that the process is still running and that he doesn't have to close the application by force. Kinda like the rotating hourglass Windows used to have back in the old days. – Robin Jul 09 '13 at 10:04
  • 2
    Then in this case the progress bar is not a suitable indicator at all. Just show an animated gif in a picture box control or create an old school *moving points* indicator in your timer **.** -> **..** -> **...** -> **.** -> and so on using a counter. Communicate with the user with text: *Please be patient. Work in progress ...*. Set the cursor to the [wait symbol](http://stackoverflow.com/questions/1568557/how-can-i-make-the-cursor-turn-to-the-wait-cursor). – keenthinker Jul 09 '13 at 10:09