Full documentation on the BackgroundWorker can be found here, but the basic usage of the BackgroundWorker is to sign up to the DoWork eventhandler with the long-running task, and call the RunWorkerAsync method to kick the task off.
A naive example:
private void btn_Click(object sender, EventArgs e) { BackgroundWorker backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork+=delegate { for(int i = 0 ; i < 10; i++) { label1.Text = DateTime.Now.ToString(); Thread.Sleep(1000); } }; backgroundWorker.RunWorkerAsync(); }
This will not work - we will get an InvalidOperationException at runtime - "Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on.". The issue being that attempting to write to the control label1 is not allowed due to the "legacy" issues that Windows leaves us. Incidentally, we get the same issue in WPF.
We need to make sure that we do not call back to the UI thread, except on the event handlers that Microsoft has kindly made for us, in this case ProgressChanged and RunWorkerCompleted.
A non-crashing example:
private void btn_Click(object sender, EventArgs e) { BackgroundWorker backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork += delegate { for(int i = 0; i < 10 ; i++) { backgroundWorker.ReportProgress(i, DateTime.Now.ToString()); Thread.Sleep(1000); } }; backgroundWorker.WorkerReportsProgress = true; backgroundWorker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args) { label1.Text = args.UserState.ToString(); }; backgroundWorker.RunWorkerCompleted += delegate { label1.Text = "Completed"; }; backgroundWorker.RunWorkerAsync(); }
If you can't guarantee that your code that invokes a BackgroundWorker will be on the UI thread, you will need to test this with InvokeRequired which is a property on the base class Control, and if necessary call the method Invoke, which calls your UI update code on the main UI thread. Invoke is a fairly expensive operation, so I don't recommend just calling Invoke regardless of whether it is needed. An example of this:
backgroundWorker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args) { if(InvokeRequired) { Invoke(new Action<object>(UpdateLabel), args.UserState); } else { UpdateLabel(args.UserState); } };
Using these handful of techniques, you should be able to build rock-solid, responsive User Interfaces, until you need to share data between threads that is, but that is a different issue alltogether. Are there any other techniques you use for responsiveness applications with Windows Forms?