简体   繁体   中英

Force GUI update from UI Thread

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

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

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: Why won't 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.

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.

设置标签后,调用Application.DoEvents() ,但是您应该在单独的线程中完成所有工作,以便用户可以关闭窗口。

I've just stumbled over the same problem and found some interesting information and I wanted to put in my two cents and add it here.

First of all, as others have already mentioned, long-running operations should be done by a thread, which can be a background worker, an explicit thread, a thread from the threadpool or (since .Net 4.0) a task: Stackoverflow 570537: update-label-while-processing-in-windows-forms , so that the UI keeps responsive.

But for short tasks there is no real need for threading although it doesn't hurt of course.

I have created a winform with one button and one label to analyze this problem:

System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
{
  label1->Text = "Start 1";
  label1->Update();
  System::Threading::Thread::Sleep(5000); // do other work
}

My analysis was stepping over the code (using F10) and seeing what happened. And after reading this article Multithreading in WinForms I have found something interesting. The article says at the bottom of the first page, that the UI thread can not repaint the UI until the currently executed function finishes and the window is marked by Windows as "not responding" instead after a while. I have also noticed that on my test application from above while stepping through it, but only in certain cases.

(For the following test it is important to not have Visual Studio set to fullscreen, you must be able to see your little application window at the same time next to it, You must not have to switch between the Visual Studio window for debugging and your application window to see what happens. Start the application, set a breakpoint at label1->Text ... , put the application window beside the VS window and place the mouse cursor over the VS window.)

  1. When I click once on VS after app start (to put the focues there and enable stepping) and step through it WITHOUT moving the mouse, the new text is set and the label is updated in the update() function. This means, the UI is repainted obviously.

  2. When I step over the first line, then move the mouse around a lot and click somewhere, then step further, the new text is likely set and the update() function is called, but the UI is not updated/repainted and the old text remains there until the button1_click() function finishes. Instead of repainting, the window is marked as "not responsive"! It also doesn't help to add this->Update(); to update the whole form.

  3. Adding Application::DoEvents(); gives the UI a chance to update/repaint. Anyway you have to take care that the user can not press buttons or perform other operations on the UI that are not permitted!! Therefore: Try to avoid DoEvents()! , better use threading (which I think is quite simple in .Net).
    But ( @Jagd, Apr 2 '10 at 19:25 ) you can omit .refresh() and .invalidate() .

My explanations is as following: AFAIK winform still uses the WINAPI function. Also MSDN article about System.Windows.Forms Control.Update method refers to WINAPI function WM_PAINT. The MSDN article about WM_PAINT states in its first sentence that the WM_PAINT command is only sent by the system when the message queue is empty. But as the message queue is already filled in the 2nd case, it is not send and thus the label and the application form are not repainted.

<>joke> Conclusion: so you just have to keep the user from using the mouse ;-) <>/joke>

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.

After updating the UI, start a task to perform with the long running operation:

label.Text = "Please Wait...";

Task<string> task = Task<string>.Factory.StartNew(() =>
{
    try
    {
        SomewhatLongRunningOperation();
        return "Success!";
    }
    catch (Exception e)
    {
        return "Error: " + e.Message;
    }
});
Task UITask = task.ContinueWith((ret) =>
{
    label.Text = ret.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());

This works in .NET 3.5 and later.

想要“修复”此问题并强制执行UI更新是非常诱人的,但是最好的解决方法是在后台线程上执行此操作,而不要占用UI线程,以便它仍然可以响应事件。

Think I have the answer, distilled from the above and a little experimentation.

progressBar.Value = progressBar.Maximum - 1;
progressBar.Maximum = progressBar.Value;

I tried decrementing the value and the screen updated even in debug mode, but that would not work for setting progressBar.Value to progressBar.Maximum , because you cannot set the progress bar value above the maximum, so I first set the progressBar.Value to progressBar.Maximum - 1, then set progressBar.Maxiumum to equal progressBar.Valu e. They say there is more than one way of killing a cat. Sometimes I'd like to kill Bill Gates or whoever it is now :o).

With this result, I did not even appear to need to Invalidate() , Refresh() , Update() , or do anything to the progress bar or its Panel container or the parent Form.

If you only need to update a couple controls, .update() is sufficient.

btnMyButton.BackColor=Color.Green; // it eventually turned green, after a delay
btnMyButton.Update(); // after I added this, it turned green quickly

I had the same problem with property Enabled and I discovered a first chance exception raised because of it is not thread-safe . I found solution about "How to update the GUI from another thread in C#?" here https://stackoverflow.com/a/661706/1529139 And it works !

myControlName.Refresh() is a simple solution to update a control before moving on to a "SomewhatLongRunningOperation". From: https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.windows.forms.control.update?view=windowsdesktop-6.0<\/a> There are two ways to repaint a form and its contents:

  1. <\/li>
  2. This is equivalent to setting the Invalidate method to true and using it with Update.<\/li><\/ol>

    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.

    "

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM