dbk*_*bkk 69 c# multithreading winforms
在WinForms中,如何从UI线程强制立即更新UI?
我正在做的大致是:
label.Text = "Please Wait..."
try
{
SomewhatLongRunningOperation();
}
catch(Exception e)
{
label.Text = "Error: " + e.Message;
return;
}
label.Text = "Success!";
Run Code Online (Sandbox Code Playgroud)
在操作之前,标签文本未设置为"Please Wait ...".
我使用另一个线程进行操作解决了这个问题,但它变得毛茸茸,我想简化代码.
Jag*_*agd 94
起初我想知道为什么OP还没有将其中一个响应标记为答案,但是在自己尝试并且仍然无法工作之后,我进一步挖了一下,发现这个问题还有更多,然后我先做应该.
通过阅读类似的问题可以获得更好的理解:为什么不能控制更新/刷新中间过程
最后,为了记录,我能够通过执行以下操作来更新我的标签:
private void SetStatus(string status)
{
lblStatus.Text = status;
lblStatus.Invalidate();
lblStatus.Update();
lblStatus.Refresh();
Application.DoEvents();
}
Run Code Online (Sandbox Code Playgroud)
虽然从我的理解,这远非一个优雅和正确的方法来做到这一点.根据线程的繁忙程度,这可能会或可能不会起作用.
Dro*_*per 14
label.Invalidate
然后调用label.Update()
- 通常只有在退出当前函数但调用Update强制它在代码中的特定位置更新时才会发生更新.来自MSDN:
Invalidate方法控制绘制或重新绘制的内容.Update方法控制何时进行绘制或重新绘制.如果您同时使用Invalidate和Update方法而不是调用Refresh,那么重新绘制的内容取决于您使用的Invalidate的重载次数.Update方法只是强制立即绘制控件,但Invalidate方法控制调用Update方法时绘制的内容.
Sco*_*hic 13
Application.DoEvents()
设置标签后调用,但您应该在单独的线程中完成所有工作,因此用户可以关闭窗口.
小智 6
如果您只需要更新几个控件,.update() 就足够了。
btnMyButton.BackColor=Color.Green; // it eventually turned green, after a delay
btnMyButton.Update(); // after I added this, it turned green quickly
Run Code Online (Sandbox Code Playgroud)
我刚刚偶然发现了同样的问题,发现了一些有趣的信息,我想投入我的两分钱并将其添加到这里。
首先,正如其他人已经提到的,长时间运行的操作应该由线程完成,该线程可以是后台工作线程、显式线程、线程池中的线程或(自 .Net 4.0 起)任务:Stackoverflow 570537: update-label-while-processing-in-windows-forms,以便 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
}
Run Code Online (Sandbox Code Playgroud)
我的分析是单步执行代码(使用 F10)并查看发生了什么。在读完这篇文章《WinForms 中的多线程》之后,我发现了一些有趣的东西。文章在第一页的底部说,UI 线程无法重新绘制 UI,直到当前执行的函数完成并且窗口会在一段时间后被 Windows 标记为“无响应”。我在上面的测试应用程序中单步执行时也注意到了这一点,但仅限于某些情况。
(对于以下测试,重要的是不要将 Visual Studio 设置为全屏,您必须能够同时看到旁边的小应用程序窗口,您不必在用于调试的 Visual Studio 窗口和您的应用程序窗口之间切换。应用程序窗口看看会发生什么。启动应用程序,在 处设置断点label1->Text ...
,将应用程序窗口放在 VS 窗口旁边,并将鼠标光标放在 VS 窗口上。)
当我在应用程序启动后单击 VS 一次(将焦点放在那里并启用步进)并在不移动鼠标的情况下单步执行它时,将设置新文本并在 update() 函数中更新标签。这意味着,UI 显然被重新绘制了。
当我跨过第一行,然后将鼠标移动很多并单击某处,然后进一步前进时,可能会设置新文本并调用 update() 函数,但 UI 不会更新/重新绘制,并且旧文本保持在那里直到button1_click()函数完成。窗口被标记为“无响应”,而不是重新绘制!this->Update();
添加更新整个表单 也无济于事。
添加Application::DoEvents();
使 UI 有机会更新/重新绘制。无论如何,您必须注意用户不能在 UI 上按下按钮或执行其他不允许的操作!因此:尽量避免 DoEvents()!,最好使用线程(我认为这在.Net中非常简单)。
但是(@Jagd,2010 年 4 月 2 日 19:25)您可以省略.refresh()
和.invalidate()
。
我的解释如下: AFAIK winform仍然使用WINAPI函数。另外,关于 System.Windows.Forms Control.Update 方法的 MSDN 文章引用了 WINAPI 函数 WM_PAINT。关于 WM_PAINT 的 MSDN 文章在第一句话中指出,WM_PAINT 命令仅在消息队列为空时由系统发送。但由于第二种情况消息队列已经填满,因此不会发送,因此不会重新绘制标签和申请表。
<>joke> 结论:所以你只需要阻止用户使用鼠标即可;-) <>/joke>
归档时间: |
|
查看次数: |
114219 次 |
最近记录: |