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

时间:2012-02-09 08:16:50

标签: .net winforms multithreading progress-bar backgroundworker

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

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

首先我尝试了简单的路线:在Button_Clicked事件中完成所有处理。这样做不顺利:它有效,但是在处理时表格变得完全没有反应,它根本不关心任何用户点击。我认为这是因为它实际上忙于处理事件,因此停止处理其他事件......所以我选择了多线程。

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

所以我最终选择Thread.Join:我在表单中添加了一个,称为BackgroundWorker方法,并使用RunWorkerAsync()触发ReportProgress()事件并处理它主窗体(避免跨线程控制更新问题)。我终于能够更新进度条......但是现在这是完全异步的:进度条实际上是在工作线程调用ProgressChanged(甚至一秒!)之后的某个随机间隔更新的,这给出了特别难看的视觉效果。两个例子:

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

我在这里缺少什么?

3 个答案:

答案 0 :(得分:0)

到目前为止,处理WinForm(和WPF)应用程序异步的最佳方法是Reactive Extensions。要经历一段曲线但是一旦你得到它你将不会回去。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

嗯,你已经尝试了很多东西。以下是一些提示,可能不适合您,但可以帮助您。

要克服“从其他线程更新控制”问题,您可以在控件上使用InvokeBeingInvoke命令(我为此使用extension method)。

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