tags:

views:

220

answers:

4

I would like to use the BackgroundWorker to perform a db transaction from a GUI.

How can I command the BackgroundWorker to perform the work and then WAIT for the worker to complete while keeping the GUI responsive?

Do I have to use DoEvents for this purpose, or is there another way?

+2  A: 

I usually wait for RunWorkerCompleted in separate modal form with progress bar. This forces end user to wait for finishing your operation, but GUI is responsive in a way that you can move forms, etc.

Better solution for end user will be to render some progress in status bar for example and allow him/her to perform only certain operations which can't break your program logic.

Is this what you want?

Filip Kunc
Thanks for the reply. I would want to launch the background process and start a progress "spinning circle" and wait for the worker to complete, then stop the spinning circle, without opening a new form - the spinning circle is on the status bar of the main form.
joek1975
You can use progress bar with marquee style or setup some independent Windows.Forms.Timer for animating the spinning circle or you can use percentage report from ProgressChanged event.More is at http://msdn.microsoft.com/en-us/library/cc221403(VS.95).aspx
Filip Kunc
A: 

You use:

Application.DoEvents();

but you should read this link for a better way to update the UI from a backgroundworker thread

Basically, the code is like this:

// The declaration of the textbox.
private TextBox m_TextBox;

// Updates the textbox text.
private void UpdateText(string text)
{
    // Set the textbox text.
    m_TextBox.Text = text;
}


public delegate void UpdateTextCallback(string text);

Then, in your thread, you can call the Invoke method on m_TextBox, passing the delegate to call, as well as the parameters.

m_TextBox.Invoke(new UpdateTextCallback(this.UpdateText),
                 new object[]{”Text generated on non-UI thread.”});

Read this link also, for more information

Gabriel McAdams
Please remember to accept this answer if you found it useful.
Gabriel McAdams
+1  A: 

You can call RunWorkerAsync like so:

this.backgroundWorker1.RunWorkerAsync();

And then you'd need to use the RunWorkerCompleted event.

Here's an example from MSDN :

private void backgroundWorker1_RunWorkerCompleted(
    object sender, RunWorkerCompletedEventArgs e)
{
    // First, handle the case where an exception was thrown.
    if (e.Error != null)
    {
        MessageBox.Show(e.Error.Message);
    }
    else if (e.Cancelled)
    {
        // Next, handle the case where the user canceled 
        // the operation.
        // Note that due to a race condition in 
        // the DoWork event handler, the Cancelled
        // flag may not have been set, even though
        // CancelAsync was called.
        resultLabel.Text = "Canceled";
    }
    else
    {
        // Finally, handle the case where the operation 
        // succeeded.
        resultLabel.Text = e.Result.ToString();
    }

    // Enable the UpDown control.
    this.numericUpDown1.Enabled = true;

    // Enable the Start button.
    startAsyncButton.Enabled = true;

    // Disable the Cancel button.
    cancelAsyncButton.Enabled = false;
}

And that's it. I don't think there's any need to call DoEvents. The UI should still be responsive while the BackgroundWorker is running.

Steve Wortham
+3  A: 

Asking "How can I command the BackgroundWorker to perform the work and then WAIT for the worker to complete while keeping the GUI responsive?" is really asking "How do I use the BackgroundWorker?". That's what BackgroundWorker does.

When you write a background task, you're basically breaking up a method into four pieces:

  1. Getting ready to run the task.
  2. The task itself.
  3. Reporting progress to the UI while the task is running.
  4. Cleaning things up when the task is done.

So you're going to need to write four methods. The first is a method that creates a BackgroundWorker, adds event handlers to its DoWork, ProgressChanged, and RunWorkerCompleted events, puts the UI into whatever state it needs to be in while the task is running, and calls RunWorkerAsync to start the task.

The other three are those three event handlers. DoWork is a DoWorkEventHandler that does the work, and that calls ReportProgress whenever it needs to report its progress to the UI. ProgressChanged is a ProgressChangedEventHandler that actually updates the UI when ReportProgress gets called. And RunWorkerCompleted is a RunWorkerCompletedEventHandler that tells the UI that the job is done.

That's all there is to it.

Well, not quite all. First, you have to make sure that you check and handle the Error property in your completion handler. If you don't do this, you will have no way of knowing that your do-work method threw an exception, since it's not happening in the UI thread and thus doesn't throw an exception that you can see. (It'll show up in the Output window, if you're looking for it.)

Second, you have to make sure that the DoWorkEventHandler doesn't touch anything in the UI. This can be tricky if you're using the MVVM pattern and you haven't planned for this contingency, because through the magic of data binding you probably have things in your model that update the views that the UI is bound to, which means that manipulating the model in your do-work method is manipulating the UI. Assuming that you're implementing INotifyPropertyChanged, a good way to avoid trouble here is to build a mechanism whereby your views don't raise PropertyChanged events while a background task is running.

You also need to figure out what pieces of the UI should be disabled while the task is running. This depends on your UI. The answer might be "all of it," and it might not. One of the things that makes using a modal form for your progress indicator attractive is that it frees you up from having to figure this out, since your entire UI is disabled while the modal form is open. If you're disabling controls or turning off property-change notifications in the launch method, you'll need to turn things back on again in the completion method.

And remember: any updates that the active parts of your UI make to your data model are a potential source of cross-thread data access errors. If your UI and the background thread ever update the same object, bad things happen - they're particularly bad if you haven't handled the Error property in your completion handler, and the cross-thread exception kills the background task without your knowing it. If it sounds like I'm dwelling on this, it's because I've lost a lot of hours of my life to doing this particular thing wrong.

Robert Rossney