我偶然发现了同样的问题,发现了一些有趣的信息,我想放两分钱在这里加进去。
首先,正如其他人已经提到的那样,长时间运行的操作应该由线程完成,该线程可以是后台工作程序,显式线程,线程池中的线程或(自.Net 4.0起)任务: 池中 Stackoverflow 570537:在Windows窗体中处理时更新标签,以便UI保持响应。
但是对于简短的任务,虽然没有什么害处,但实际上并不需要线程。
我创建了一个带有一个按钮和一个标签的winform来分析此问题:
System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
label1->Text = "Start 1";
label1->Update();
System::Threading::Thread::Sleep(5000);
}
我的分析是遍历代码(使用F10)并查看发生了什么。在阅读这篇文章之后 WinForms中的多线程》之后,我发现了一些有趣的东西。文章在第一页底部说,直到当前执行的功能完成并且一段时间后Windows将窗口标记为“无响应”,UI线程才能重绘UI。我还注意到,在我的测试应用程序中,它是从上面逐步执行的,但仅在某些情况下如此。
(对于以下测试,重要的是不要将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函数。MSDN上有关System.Windows.Forms Control.Update方法的文章也引用WINAPI函数WM_PAINT。有关WM_PAINT的MSDN文章在其第一句话中指出,仅当消息队列为空时,系统才发送WM_PAINT命令。但是由于在第二种情况下消息队列已被填充,因此不会发送消息,因此不会重新绘制标签和申请表。
<>笑话>结论:因此,您只需要防止用户使用鼠标;-) <> / joke>
SomewhatLongRunningOperation()
在另一个线程中运行是正确的答案。您不应将UI线程用于任何不会直接影响UI的事情。至于简化代码,很有可能可以简化其他线程的使用。