C# 从UI线程强制更新GUI

C# 从UI线程强制更新GUI,c#,winforms,multithreading,C#,Winforms,Multithreading,在WinForms中,如何从UI线程强制立即更新UI 我所做的大致是: label.Text = "Please Wait..." try { SomewhatLongRunningOperation(); } catch(Exception e) { label.Text = "Error: " + e.Message; return; } label.Text = "Success!"; 操作前,标签文本未设置为“请稍候…” 我用另一个线程解决了这个问题,但是它

在WinForms中,如何从UI线程强制立即更新UI

我所做的大致是:

label.Text = "Please Wait..."
try 
{
    SomewhatLongRunningOperation(); 
}
catch(Exception e)
{
    label.Text = "Error: " + e.Message;
    return;
}
label.Text = "Success!";
操作前,标签文本未设置为“请稍候…”


我用另一个线程解决了这个问题,但是它会变得很复杂,我想简化代码。

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

调用
标签。使
无效,然后
标签。更新()
-通常只有在退出当前函数后才会进行更新,但调用update会强制它在代码中的特定位置进行更新。 发件人:

Invalidate方法控制绘制或重新绘制的内容。更新方法控制何时进行绘制或重新绘制。如果同时使用Invalidate和Update方法而不是调用Refresh,那么重新绘制的内容取决于使用的Invalidate重载。Update方法只是强制立即绘制控件,但是Invalidate方法控制调用Update方法时绘制的内容


想要“修复”这个问题并强制进行UI更新是很有诱惑力的,但最好的修复方法是在后台线程上执行此操作,而不是绑定UI线程,这样它仍然可以响应事件。

尝试调用label.Invalidate()

你可以试试这个

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

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

看看它是否有效。

起初,我想知道为什么OP没有将其中一个响应标记为答案,但在自己尝试后,仍然没有成功,我进一步挖掘,发现这个问题比我最初想象的要多得多

阅读类似问题可以更好地理解:

最后,为了记录在案,我可以通过执行以下操作来更新我的标签:

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

虽然据我所知,这远远不是一个优雅和正确的做法。这是一种黑客行为,可能有效,也可能无效,具体取决于线程的繁忙程度。

更新UI后,启动一项任务以执行长时间运行的操作:

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());
label.Text=“请稍候…”;
Task Task=Task.Factory.StartNew(()=>
{
尝试
{
somewhatlonglunningoperation();
返回“成功!”;
}
捕获(例外e)
{
返回“错误:”+e.消息;
}
});
任务UITask=任务。继续((ret)=>
{
label.Text=返回结果;
},TaskScheduler.FromCurrentSynchronizationContext());

这在.NET3.5和更高版本中都可以使用。

我刚刚偶然发现了同样的问题,并发现了一些有趣的信息,我想把我的两分钱放在这里

首先,正如其他人已经提到的,长时间运行的操作应该由线程完成,线程可以是后台工作线程、显式线程、线程池中的线程或(自.Net 4.0以来)任务:,以便UI保持响应

但是对于短任务来说,线程并没有真正的必要,当然这并没有什么坏处

我创建了一个带有一个按钮和一个标签的winform来分析此问题:

System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
{
  label1->Text = "Start 1";
  label1->Update();
  System::Threading::Thread::Sleep(5000); // do other work
}
我的分析是跨过代码(使用F10)看看发生了什么。读了这篇文章后,我发现了一些有趣的东西。文章在第一页的底部说,在当前执行的函数完成并且窗口被Windows标记为“未响应”之前,UI线程无法重新绘制UI。我还注意到,在我的测试应用程序中,当我逐步使用它时,从上面开始,但仅在某些情况下

(对于以下测试,重要的是不要将Visual Studio设置为全屏,您必须能够同时看到它旁边的小应用程序窗口,您不必在Visual Studio调试窗口和应用程序窗口之间切换以查看发生了什么。启动应用程序,在
label1处设置断点->Text…
,将应用程序窗口放在VS窗口旁边,并将鼠标光标放在VS窗口上。)

  • 当我在应用程序启动后单击一次VS(将焦点放在那里并启用单步执行)并在不移动鼠标的情况下单步执行它时,新文本被设置,标签在update()函数中被更新。这意味着,UI显然被重新绘制

  • 当我跨过第一行,然后将鼠标移动很多并单击某个位置,然后再进一步,可能会设置新文本并调用update()函数,但UI不会更新/重新绘制,旧文本将保留在那里,直到button1_click()函数完成。窗口不会重新绘制,而是标记为“没有响应”!添加
    this->Update();
    更新整个表单也没有帮助

  • 添加
    Application::DoEvents();
    会给UI一个更新/重新绘制的机会。无论如何,您必须注意用户不能在UI上按按钮或执行其他不允许的操作!!因此:,最好使用线程(我认为在.Net中非常简单)。
    但是(@Jagd,2010年4月2日19:25)您可以省略
    .refresh()
    .invalidate()

  • 我的解释如下:AFAIK winform仍然使用WINAPI函数。也指WINAPI函数WM_PAINT。在其第一句中,WM_PAINT命令仅在消息队列为空时由系统发送。但由于第二种情况下消息队列已填充,因此它不发送,因此标签和应用程序也不发送申请表未重新绘制


    笑话>结论:因此您只需阻止用户使用鼠标;-)/笑话>

    我在启用属性
    时遇到了相同的问题,我发现了一个
    首次机会异常
    ,因为它是非线程安全的。
    我在这里找到了关于“如何从C#中的另一个线程更新GUI”的解决方案,它很有效!

    我想我找到了答案,从
    progressBar.Value = progressBar.Maximum - 1;
    progressBar.Maximum = progressBar.Value;
    
    btnMyButton.BackColor=Color.Green; // it eventually turned green, after a delay
    btnMyButton.Update(); // after I added this, it turned green quickly