tags:

views:

111

answers:

4

Suppose a GUI (C#, WinForms) that performs work and is busy for several seconds. It will still have buttons that need to remain accessible, labels that will change, progress bars, etc.

I'm using this approach currently to change the GUI when busy:

//Generic delegates
private delegate void SetControlValue<T>(T newValue);

//...
public void SetStatusLabelMessage(string message)
{
    if (StatusLabel.InvokeRequired)
        StatusLabel.BeginInvoke(new SetControlValue<string>(SetStatusLabelMessage, object[] { message });
    else
        StatusLabel.Text = message;
}

I've been using this like it's going out of style, yet I'm not quite certain this is proper. Creating the delegate (and reusing it) makes the whole thing cleaner (for me, at least), but I must know that I'm not creating a monster...

+1  A: 

It looks right to me. So long as you are encapsulating that sort of thing into its own well-tested class, there should be no problem with it.

By "into its own class", do you mean as opposed to the form class? I'm not for cramming a maximum of code in the form class, quite the contrary, but if it *controls* the form, I have a habit of keeping it *in* the form class.
MPelletier
@MPelletier: I meant keep that code, which is UI-implementation specific, away from the application and business logic, which should already have been separated out into its own class. So, actually, putting that stuff in the form class is fine.
+5  A: 

Another alternative is to use a BackgroundWorker and use ReportProgress when you need to update the GUI. This will handle the Invoke for you so that you don't have to worry about it.

Mark Byers
I have a tough time reconciling BackgroundWorkers and other threading used in the program. As a well-travelled gentleman programmer, which do you see more often? Control via BackgroundWorker or Invoke?
MPelletier
@MPelletier: BackgroundWorker for this sort of task - you have a long running task, you need to keep the GUI reponsive, and you need to show a progress bar. It's exactly what BackgroundWorker was designed for.
Mark Byers
I took the question to be more of, "what is the best practice for updating the UI from the background thread". This answer does not really address that question.
Justin Ethier
+2  A: 

Best practice here is to use a BackgroundWorker to run the long-running task. It's a special threading class set up specifically for WinForms, just for this purpose. You can send work to it, cancel it, and also let it send messages back to the WinForm thread to update status. Basically, by using a BackgroundWorker, your GUI stays free and active.

Cylon Cat
But he is already doing exactly this, although with a different class than `BackgroundWorker`.
Justin Ethier
He asked for a "best practice." BackgroundWorker is "best practice."
Cylon Cat
+2  A: 

Others have mentioned using BackgroundWorker to run your work in. Is that what you are doing?

In any case, if you are you wondering what the best way to update the UI from your background thread is, what you are doing looks fine, although I recommend using Invoke instead of BeginInvoke. Also, instead of declaring your own separate delegate, you can use MethodInvoker to simplify your code:

StatusLabel.Invoke(new MethodInvoker(delegate() { /* Update control here */ }));
Justin Ethier
Not using the BackgroundWorker, no. In a case where the BGW would just call a single, linear function, I'd do it, but I hesitate in some program where the background function uses a ThreadPool. At that level, I feel like I'm layering structures unnecessarily. Thanks for the Invoke/BeginInvoke, which I need to refresh in my mind to better appreciate the difference. MethodInvoker looks fine too!
MPelletier
No problem. Does this help answer your question, or is there something else you were looking for?
Justin Ethier