views:

1097

answers:

7

In WinForms, how do I force an immediate UI update from UI thread?

What I'm doing is roughly:

label.Text = "Please Wait..."
try 
{
    SomewhatLongRunningOperation(); 
}
catch(Exception e)
{
    label.Text = "Error: " + e.Message;
    return;
}
label.Text = "Success!";

Label text does not get set to "Please Wait..." before the operation.

I solved this using another thread for the operation, but it gets hairy and I'd like to simplify the code.

+2  A: 

Call Application.DoEvents() after setting the label, but you should do all the work in a separate thread instead, so the user may close the window.

Scoregraphic
+1, but it is for simple applications it is completely valid to keep things simple by not using background worker thread. Just set the cursor to an hourglass call Application.DoEvents.
Ash
No, it's not, since the SomeWhatLongRunningOperation is blocking the whole application. Do you like non-responding programs? I don't!
Scoregraphic
No I just don't like over-complicating simple applications that simply don't require the overhead of multiple threads. Windows is a multi-tasking OS, just kick off the task and do some other work done in another app.
Ash
I would argue that the simple application makes for excellent training ground to get things like threading right, so that you don't have to experiment in more critical code. Furthermore, `Application.DoEvents` may introduce interesting issues (that would also occur with threading), such as what happens if the user click a button that triggers the operation again, which it is still running?
Fredrik Mörk
@Frederik, where I work, if you were to do "training exercises" by introducing more complicated code to simple applications you would be quickly given a "kick up the rear".
Ash
@Ash: Have you ever had a look at the thread count after starting a simple .NET forms application? So tell us how you would introduce yourself into multithreaded development if not using a simple excercise? I'm curious.
Scoregraphic
+4  A: 

Call label.Invalidate and then label.Update() - usually the update only happens after you exit the current function but calling Update forces it to update at that specific place in code. From MSDN:

The Invalidate method governs what gets painted or repainted. The Update method governs when the painting or repainting occurs. If you use the Invalidate and Update methods together rather than calling Refresh, what gets repainted depends on which overload of Invalidate you use. The Update method just forces the control to be painted immediately, but the Invalidate method governs what gets painted when you call the Update method.

Dror Helper
+1  A: 

It's very tempting to want to "fix" this and force a UI update, but the best fix is to do this on a background thread and not tie up the UI thread, so that it can still respond to events.

Mike Hall
Overkill, for a simple application it is completely valid to force a UI update. The minimize/maximize buttons still work, just work on something else. Why introduce inter thread communication issues?
Ash
I wouldn't call it overkill for a simple application, only overkill if the operation is a really short one. However, he called it a "SomewhatLongRunningOperation". And since people usually only need to do this if the UI will be tied up for a somwhat long period of time, why lock the UI at all? That's what the BackgroundWorker class is for! Can't get much easier than that.
Mike Hall
A: 

Update() - Causes the control to redraw the invalidate regions within its client are.

label1.Text = "Started...";
label1.Update();
for (int i = 1; i <= 100000; i++){
  for (int j = 1; j <= 600; j++){}
}
label1.Text = "Stopped.";
adatapost
A: 

you can try this

using System.Windows.Forms; // u need this to include.

MethodInvoker updateIt = delegate
                {
                    this.label1.Text = "Started...";
                };
this.label1.BeginInvoke(updateIt);

See if it works.

Rahul2047
+3  A: 

At first I wondered why the OP hadn't already marked one of the responses as the answer, but after trying it myself and still have it not work, I dug a little deeper and found there's much more to this issue then I'd first supposed.

A better understanding can be gained by reading from a similar question: http://stackoverflow.com/questions/2341731/why-wont-control-update-refresh-mid-process

Lastly, for the record, I was able to get my label to update by doing the following:

private void SetStatus(string status) {
            lblStatus.Text = status;
            lblStatus.Invalidate();
            lblStatus.Update();
            lblStatus.Refresh();
            Application.DoEvents();
        }

Though from what I understand this is far from an elegant and correct approach to doing it. It's a hack that may or may not work depending upon how busy the thread is.

Jagd
Thanks, it actually works. I re-structured the code to get around this, but your solution (hacky as it may be) is more elegant.
dbkk