views:

184

answers:

4

I'm writting a Windows Forms application in C# that performs a lot of long-running procedures. I need to program the application so that the GUI doesn't lock. What is the best way to program it?

I know how to use the following:

  1. BeginInvoke/EndInvoke
  2. Calling Application.DoEvents() repeatedly (probably not a good idea)
  3. BackgroundWorker
  4. etc.

But how to manage GUI state with call backs, etc... is not trivial. Are there solutions for this (in the form of patterns or libraries)?

+1  A: 

The Model-View-Controller pattern separates the state of your UI from the visual aspects of the UI. As long as your MVC implementation is thread aware, it should solve the state management issue for you.

This is how I handle multi-threaded UI implementations.

EDIT: Here's a good post on selecting an MVC implementation for WinForms projects.

Eric J.
aha... that'w what MVC is for. OK.. Many Thanks.
Nestor
A: 

It is relatively simple to use the ThreadPool to fire off long running processes from the UI. If you want feedback you can use some event handlers to fire on certain events from the long running process, then register for them and update the UI as needed.

MVC is good, but you still need to make sure your process is firing off on a thread other than the UI thread.

mrnye
A: 

All your options are syntactic sugar of doing same thing (Asynchronous execution). May be with different levels of controls. I would go with BackgroundWorker, because your GUI (handled by the main thread) will always be responsive.

Lav G
+2  A: 

Using BackgroundWorker is the simplest way to do what you're trying to do. BackgroundWorker simplifies the logic of dealing with threads, leaving you with very little code you have to write. You just have to handle three events (DoWork, ProgressChanged, and RunWorkerCompleted) and follow a few principles:

  • Don't ever update the UI inside your long-running method. Have it call ReportProgress instead and handle the ProgressChanged event in the UI logic.
  • Understand that since the worker isn't running on the UI thread, an exception that your method throws doesn't automatically show up in the UI. It just gets silently written to the console. It's very important to check the Error property of the RunWorkerCompletedEventArgs when the method is done and handle (or throw) the exception. If you don't do this, you won't know that your method failed.
  • If the method can be cancelled, make sure that it periodically checks the CancellationPending property to see if cancellation has been requested. If it has, once it's done handling the cancellation, set the Cancel property on the DoWorkEventArgs object. Be aware that it's possible for CancellationPending to be true and Cancel to be false; this happens, for instance, when the user requests cancellation and the method finishes before it has a chance to check CancellationPending.
  • Correspondingly, check Cancel in your RunWorkerCompleted event handler, so that your UI can provide the proper response to what happened while the worker was running.

The examples in the documentation all show the DoWork event handler calling a method of the form. This is almost certainly not what you want to do. Separating business logic from UI is a good idea in general; in background tasks it's essential. If your long-running method is a member of a class that doesn't know that the UI even exists, there's no danger that you'll inadvertently have it call a method that updates a status bar or something.

Robert Rossney