我有一个线程处理二进制文件并将其存储在数据表中。
处理时我正在使用当前流位置更新进度条。
我的问题是;在线程中我有一个finally {}块,并且我将progressbar值设置为最大允许值,然后我运行Application.DoEvents();
然后我显示一个MessageBox,但我的MessageBox总是在Progressbar填充之前出现。
here is a video showing the issue
我创建了一个小应用程序来演示使用BackgroundWorker而不是Thread。 Here is the code
我看到了相同的'错误'(我犹豫不决称这是一个错误,更多的是可用性问题)正如你所看到的那样,我还在那里添加了一个 dirty 小记录器。日志文件末尾的输出是:
27/10/2011 4:58:29 PM - 进入进度
27/10/2011 4:58:29 PM - 进度已更改为79661
27/10/2011 4:58:29 PM - 进入进度
27/10/2011 4:58:29 PM - 进度已更改为79661
27/10/2011 4:58:29 PM - 完成输入
27/10/2011 4:58:29 PM - 事件完成
27/10/2011 4:58:30 PM - 显示消息
因此执行命令的顺序没有问题...是我在显示消息框之前添加延迟的唯一真正解决方案吗?
答案 0 :(得分:5)
我会尝试用简单的Application.DoEvents();
替换Thread.Sleep(100)
。围绕DoEvents
的文档似乎并未表明是否允许您从另一个非UI线程调用它。通过使用Thread.Sleep
代替,您将延迟消息框弹出窗口并允许UI线程正常处理。
让工作者背景弹出消息框的另一种方法是使用一个事件,并在UI线程上调用已订阅的委托(就像你对进度条所做的那样)。
实际上,如果您愿意,您可以重写线程代码以使用BackgroundWorker对象,并订阅ProgressChanged
和RunWorkerCompleted
事件。您的实际线程代码将负责调用ReportProgress
方法来触发ProgressChanged
事件以更新您的进度条。
查看新代码后,当工人完成时,您永远不会将进度条设置为最大值。在显示消息框之前worker_RunWorkerCompleted
添加progressBar1.Value = progressBar1.Maximum;
。这应该在弹出消息框之前将进度条更新到最大值。
答案 1 :(得分:2)
正如Joshua建议的那样,你不能从与你的UI不同的线程调用DoEvents。因此,我(基于这个事实)建议您在线程上使用Join()
方法等待(从UI线程)直到它结束。
您还可以使用ThreadDispatcher(可能仅限WPF)或使用表单上的Invoke()
方法从辅助线程调用主UI线程上的方法。
修改强> 另一个解决方案是创建一个看起来像MessageBox的自己的表单,然后让该窗口正常显示,这样它就不会阻塞UI线程。