I have a windows form on the main thread and another thread that does some calculations. I'd like to update the status bar on my form from the work being done in the other thread. What's the best way to do this?
So far everything I've tried just makes things run really slowly. I'm using Visual Studio 2005.
Make sure that you only update the user interface from the main thread or else you will have problems. You can switch your thread context by calling Invoke. There's a good post here on that.
You can use the marshaling techniques like Control.Invoke
to execute a delegate on the UI thread where UI elements can be safely manipulated, but that approach is not very good. Actually, it is a terrible approach if all you want to do is update simple progress information.
By far the best method for doing this is:
- Have your worker thread publish progress information to a shared variable.
- Have your UI thread poll for it via a
System.Windows.Forms.Timers
on an interval that works well for you.
Here is what it might look like.
public class Example : Form
{
private volatile int percentComplete = 0;
private void StartThreadButton_Click(object sender, EventArgs args)
{
StatusBarUpdateTimer.Enabled = true;
new Thread(
() =>
{
for (int i = 1; i <= 100; i++)
{
DoSomeWork();
percentComplete = i;
}
}).Start();
}
private void StatusBarUpdateTimer_Tick(object sender, EventArgs args)
{
yourStatusBarPanel.Text = percentComplete.ToString() + "%";
StatusBarUpdateTimer.Enabled = percentComplete < 100;
}
}
This works well because:
- The UI thread gets to dictate when and how often the UI gets updated...the way it should be!
- The worker thread does not have to wait for a response from the UI thread before it can proceed as would be the case with
Invoke
.
- It breaks the tight coupling between the UI and worker threads that
Invoke
would impose.
- It is more efficient...considerably.
- You get more throughput on both the UI and worker threads.
- There is no chance of saturating the UI message queue as could be the case with
BeginInvoke
.
- You do not have to litter you code with
Invoke
calls everytime you need to update the UI from the worker thread.
You can send messages to the main thread and get it to update the progress bar, although you then need to check for the messages. You could also do the same sort of thing as a polling function.