views:

768

answers:

4

Say I'm showing the user a form, and using a BackgroundWorker to do some work behind the scenes.

When the user clicks OK, I cannot continue until the BackgroundWorker has completed. If it hasn't finished when the user clicks Ok, I want to show a WaitCursor until it has, and then continue.

What's the best way to implement this?

I know I could use a plain old Thread, and then do Thread.Join, but I like BackgroundWorker.

Can it be done nicely?

+5  A: 

Ideally, you should disable all the relevant controls on the form, then re-enable them when the BackgroundWorker completes. That way you won't get a locked UI - and you can have a cancel button etc.

If you wanted to actually block until it had finished, you could just run the task synchronously to start with.

Jon Skeet
I don't want the user to have to wait for the Ok button to enable itself though. I don't want to have to explain about the background process.
Blorgbeard
I'm using this approach and very happy with it. You shouldn't explain about background processes - simply add progress bar.
Sergey Mirvoda
The progress bar would require explanation. The user should notice nothing different except that clicking Ok causes little to no delay now, whereas before it took a few seconds.
Blorgbeard
But if you block the UI thread, the user won't see the button being pressed - they'll click on it and see no visible change, because you'll be blocking the event loop.
Jon Skeet
Good point. I guess I could call Application.DoEvents in a loop or something..
Blorgbeard
As for DoEvents... http://www.codinghorror.com/blog/archives/000159.html
Sergey Mirvoda
"I agree that DoEvents is not exactly great programming practice, but even Microsoft recommends using it in lieu of hard-core threading for simple problems."
Blorgbeard
But I agree, that's not optimal by any stretch.. I'll look into a solution later.
Blorgbeard
+5  A: 

You could use this code, instead of BW

var resetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(state =>
       {
        Thread.Sleep(5000);//LongRunning task, async call
        resetEvent.Set();
       });
resetEvent.WaitOne();// blocking call, when user clicks OK
Sergey Mirvoda
A: 

I'd just use boolean flags and a lock.

object waitlock = new object();
bool isBackgroundWorkerCompleted = false;
bool isOKButtonPressed = false;

private void onPress(...) 
{
    lock(waitlock) 
    {
      isOKButtonPressed = true;
      if (isBackgroundWorkerCompleted) DoNextStep();
      else cursor.Wait(); //psuedo-code - use whatever the actual call is...
    }
}

Background thread does the same trick (just remember to BeginInvoke back to the main UI thread)

A: 

You could check is the BackgroundWorker is running with a check in the btnOK_Click event handler to see if bw.IsBusy is true. If so change the cursor and set a boolean flag that indicates that OK handler is waiting. When BackgroundWorker finishes its job check if ok had been waiting, if so, set the flag to false change the cursor and do the job of the OK button. :-)

Gaurav