如何在处理时处理控件更新



我正在编写一个小型.NET(Windows窗体)应用程序,但似乎无法正确处理多线程。

该应用程序有一个非常简单的逻辑:它显示一个主表单,其中包含一些要填写的参数、一个开始处理的按钮和一个进度条;当用户点击按钮时,处理开始,程序运行时应更新进度条,处理时可以弹出一些MessageBox es以显示警告/信息,当处理结束时,会弹出另一个MessageBox并显示结果。相当简单。。。但我似乎找不到正确处理控制更新的方法。

首先,我尝试了一种简单的方法:在Button_Clicked事件中进行所有处理。这并不顺利:它起作用了,但表单在处理过程中完全没有响应,它根本不在乎任何用户点击。我认为这是因为它实际上正忙于处理一个事件,从而停止处理其他事件。。。所以我选择了多线程。

在按钮处理事件中,我启动了一个新的Thread并让它进行处理,然后在它上调用Thread.Join;MSDN documentatin谈到Thread.Join时说:"阻塞调用线程,直到线程终止,同时继续执行标准的COM和SendMessage泵浦。"(http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx)。但情况似乎并非如此:在调用Thread.Join之后,表单再次变得没有响应,直到线程终止。为什么会这样?我放弃了Join的想法,立即从事件处理功能返回(禁用表单上的按钮以避免用户同时启动多个处理),但后来我遇到了另一个问题:我在尝试更新进度条时开始出现异常,运行时抱怨从不是其所有者的线程调用了控件。

所以我最后使用了一个BackgroundWorker:我在表单中添加了一个,调用了它的RunWorkerAsync()方法,并使用ReportProgress()来激发ProgressChanged事件并在主表单中处理它(以避免跨线程控制更新问题)。我终于可以更新进度条了。。。但现在这是完全异步的:在工作线程调用ReportProgress()(甚至一秒钟!)后,进度条实际上会以某个随机间隔更新,这会产生特别难看的视觉效果。两个例子:

  • 如果我从工作线程调用ReportProgress(),然后调用MessageBox.Show()来显示有关当前处理步骤的一些消息,我可以看到进度条在弹出MessageBox后前进
  • 我有一个RunWorkerCompleted事件,它会弹出最后一个带有结果的MessageBox,我可以看到它在屏幕上弹出,而进度条仍在填充其最后步骤

我在这里错过了什么?

IMHO,到目前为止,在WinForm(和WPF)应用程序中处理异步的最好方法是Reactive Extensions。这是一条曲线,但一旦你得到了它,你就不会回头了。

添加Application.DoEvents()以更新需要的UI。

您已经尝试了很多方法。这里有一些建议,也许对你来说不是正确的答案,但可以帮助你。

为了克服"从其他线程更新控件"的问题,可以在控件上使用InvokeBeingInvoke命令(我对此使用了扩展方法)。

除此之外,我认为Kangkan的评论是合法的——使用MessageBox只会给你的问题增加痛苦,因为它是模态的,它会阻止UI更新。你确定MessageBox是合适的吗?在继续之前,你绝对需要用户输入吗?还是你只需要通知他们?如果是后者,您应该考虑使用更像状态栏的东西,或者您在StackOverflow上收到的系统消息(想象一下,如果它们都是警报!)

最新更新