I'm trying to have a seperate thread in a WinForms C# application start a background worker which controls a ProgressBar (marquee). The issue is that when i try to set the bar to visible it just does nothing, and i've tried many forms of Invoke but they don't seem to help.
The following method progressBarCycle
is called from a separate thread.
BackgroundWorker backgroundWorker = new BackgroundWorker();
public void progressBarCycle(int duration)
{
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.RunWorkerAsync(duration);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(0);
DateTime end = DateTime.Now.AddMilliseconds((int)e.Argument);
while (DateTime.Now <= end)
{
System.Threading.Thread.Sleep(1000);
}
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!this.IsHandleCreated)
this.CreateHandle();
statusStrip1.Invoke((MethodInvoker)delegate
{
progressBar1.Visible = false;
});
// if (!this.IsHandleCreated)
// {
// this.CreateHandle();
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false));
// else progressBar1.Visible = false;
// }
// else
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false));
// else progressBar1.Visible = false;
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (!this.IsHandleCreated)
this.CreateHandle();
statusStrip1.Invoke((MethodInvoker)delegate
{
progressBar1.Visible = true;
});
// if (!this.IsHandleCreated)
// {
// this.CreateHandle();
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true));
// else progressBar1.Visible = true;
// }
// else
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true));
// else progressBar1.Visible = true;
}
Am I missing something obvious here? The comment sections are other things I've tried.
progressBarCycle
from the UI thread.RunWorkerAsync
will create the new thread for you.backgroundWorker_ProgressChanged
simply callprogressBar1.Visible = true;
. There is no need forInvoke
.progressBar1.Refresh();
.ProgressChanged
is already raised on the UI thread (via the sync-context); yourProgressChanged
does not need to do thatInvoke
- it can manipulate the UI directly (by contrast,DoWork
can absolutely not do that). Perhaps the real problem is that you don't do anyworker.ReportProgress(...)
inside the loop - so it only happens once at the start.Here's a full example:
Another possibility to be aware of is that the progress bar is running on your UI thread. In order for the progress bar to be displayed and redraw itself to show new progress amounts, the UI thread must be running freely, processing windows messages in the main application loop.
So if you start your background worker thread but then your UI thread sits in a busy wait loop waiting for it to complete, or goes off and does loads of other work, then it won't be processing windows messages and your progress bar will be "unresponsive". You need to release the UI thread so that this updating still happens (i.e. return from your event handler and allow the UI to continue running as normal).
The danger of this is that if the UI is active, then the user can still interact with it. You therefore have to write the UI to be aware when the background worker is active, and handle the situation properly (problems can include: Allowing the user to start the background worker again while it is already running, UI trying to display information while the worker thread is busily updating it, the user deciding to load a new document or quit while the background worker is busy, etc). The two main solutions to this are to wrap every bit of UI in a protective shield that stops anything dangerous being initiated while the background work is running (this can be a lot of work if you have lots of controls to wrap in this way, and it's easy to make a mistake that lets a bug slip through) or to leave the UI "unprotected" but add an IMessageFilter that stops all "dangerous" user interaction (clicks and keypresses) by suppressing their incoming windows messages (WM_KEYPRESS etc) while the background processing is active.