使用Application.DoEvents()

Cra*_*ton 263 c# doevents winforms

可以Application.DoEvents()在C#中使用吗?

这个函数是否能够让GUI跟上应用程序的其余部分,就像VB6 DoEvents一样?

Han*_*ant 458

Hmya,DoEvents()的持久神秘感.对它有强烈的反对意见,但没有人真正解释为什么它"糟糕".与"不要改变结构"相同的智慧.嗯,为什么运行时和语言支持变异结构,如果那么糟糕?同样的道理:如果你做得不对,你会在脚下射击.容易.做正确的事情需要确切知道它的作用,在DoEvents()的情况下,这绝对不容易理解.

马上就是:几乎任何Windows窗体程序实际上都包含对DoEvents()的调用.它巧妙地伪装,但使用不同的名称:ShowDialog().DoEvents()允许对话框是模态的,而不会冻结应用程序中的其余窗口.

大多数程序员希望在编写自己的模态循环时使用DoEvents来阻止用户界面冻结.当然这样做; 它调度Windows消息并获取任何绘制请求.但问题是它没有选择性.它不仅可以发送绘制消息,还可以提供其他所有内容.

并且有一组导致麻烦的通知.它们来自显示器前方约3英尺处.例如,当调用DoEvents()的循环正在运行时,用户可以关闭主窗口.这工作,用户界面消失了.但是你的代码没有停止,它仍在执行循环.那很糟.非常非常糟糕.

还有更多:用户可以单击导致相同循环开始的相同菜单项或按钮.现在你有两个执行DoEvents()的嵌套循环,前一个循环被挂起,新循环从头开始.这可行,但男孩的可能性很小.特别是当嵌套循环结束并且暂停的循环重新开始时,尝试完成已完成的作业.如果没有异常爆炸,那么数据肯定会被打乱.

回到ShowDialog().它执行DoEvents(),但请注意它执行其他操作.它会禁用应用程序中的所有窗口,而不是对话框.现在解决了3英尺的问题,用户无法做任何事情来搞乱逻辑.关闭窗口和启动作业再次失败模式都得到了解决.或者换句话说,用户无法使程序以不同的顺序运行代码.它将以可预测的方式执行,就像测试代码时一样.它使对话非常烦人; 谁不讨厌激活对话而无法从另一个窗口复制和粘贴东西?但那就是价格.

这是在代码中安全使用DoEvents所需的.将所有表单的Enabled属性设置为false是避免问题的快速有效方法.当然,没有程序员真正喜欢这样做.并没有.这就是你不应该使用DoEvents()的原因.你应该使用线程.即使他们为你提供了一套完整的方法,以丰富多彩,不可思议的方式射击你的脚.但是你的优点是你只能射击自己的脚; 它不会(通常)让用户拍摄她的照片.

下一版本的C#和VB.NET将使用新的await和async关键字提供不同的枪支.所造成的的DoEvents和线程,但通过WinRT中的API设计大的一部分,麻烦少部分的启发,需要你保持你的UI更新,而异步操作正在发生.喜欢从文件中读取.

  • 与通常"使用线程"相比,将从更实际的解决方案中受益.例如,`BackgroundWorker`组件可以为您管理线程,避免了足部射击的大部分结果,并且它不需要尖端的C#语言版本. (4认同)
  • TL;DR:使用“Application.DoEvents”是可以的,前提是您是一位认真负责、遵守纪律、受过良好教育且具有良好判断力的开发人员。问题是“Application.DoEvents”对于至少缺乏其中一项品质(至少暂时)的开发人员特别有吸引力。 (4认同)

RQD*_*QDQ 29

它可以,但它是一个黑客.

DoDovents邪恶吗?.

直接在MSDN页面thedev引用:

调用此方法会导致在处理所有等待窗口消息时挂起当前线程.如果消息导致触发事件,则可以执行应用程序代码的其他区域.这可能会导致您的应用程序出现难以调试的意外行为.如果执行需要很长时间的操作或计算,通常最好在新线程上执行这些操作.有关异步编程的更多信息,请参阅异步编程概述.

所以微软警告不要使用它.

此外,我认为它是一个黑客,因为它的行为是不可预测的和副作用倾向(这来自尝试使用DoEvents而不是启动新线程或使用后台工作程序的经验).

这里没有大男子主义 - 如果它作为一个强大的解决方案,我会全力以赴.但是,尝试在.NET中使用DoEvents只会给我带来痛苦.


Bil*_*l W 23

是的,System.Windows.Forms命名空间中的Application类中有一个静态DoEvents方法.在UI线程中执行长时间运行的任务时,System.Windows.Forms.Application.DoEvents()可用于处理UI线程上队列中等待的消息.这样做的好处是,在长任务运行时,UI看起来更具响应性并且不会"锁定".但是,这几乎总是不是最好的做事方式.根据微软调用DoEvents"...导致当前线程被暂停,同时处理所有等待窗口消息." 如果触发事件,则可能会出现难以追踪的意外和间歇性错误.如果你有一个广泛的任务,最好在一个单独的线程中完成它.在单独的线程中运行长任务允许在不干扰UI继续平稳运行的情况下处理它们.看这里了解更多详情.

以下是如何使用DoEvents的示例; 请注意,Microsoft也提供了使用它的警告.


Cra*_*ton 13

根据我的经验,我建议在.NET中使用DoEvents时要非常谨慎.在包含DataGridViews的TabControl中使用DoEvents时,我遇到了一些非常奇怪的结果.另一方面,如果你所处理的只是一个带有进度条的小表格,那么它可能没问题.

底线是:如果您要使用DoEvents,则需要在部署应用程序之前对其进行彻底测试.


Fre*_*els 11

是.

但是,如果您需要使用Application.DoEvents,这主要表明应用程序设计不良.也许你想在一个单独的线程中做一些工作呢?

  • 如果你想要它,那么你可以旋转并等待工作在另一个线程中完成? (3认同)

小智 5

我在上面看到了 jheriko 的评论,并且最初同意如果您最终旋转主 UI 线程等待另一个线程上长时间运行的异步代码完成,我无法找到避免使用 DoEvents 的方法。但是从 Matthias 的回答来看,我的 UI 上一个小面板的简单刷新可以替换 DoEvents(并避免令人讨厌的副作用)。

关于我的案例的更多细节......

我在做以下操作(建议在这里),以确保进度条型闪屏(如何显示“加载”覆盖...)长时间运行的SQL命令时更新:

IAsyncResult asyncResult = sqlCmd.BeginExecuteNonQuery();
while (!asyncResult.IsCompleted)  //UI thread needs to Wait for Async SQL command to return
{
      System.Threading.Thread.Sleep(10); 
      Application.DoEvents();  //to make the UI responsive
}
Run Code Online (Sandbox Code Playgroud)

坏处:对我来说,调用 DoEvents 意味着鼠标点击有时会在我的启动画面后面的表单上触发,即使我将它设为 TopMost。

好的/答案:用一个简单的 Refresh 调用替换 DoEvents 行,调用我的启动屏幕中心的一个小面板FormSplash.Panel1.Refresh()。UI 更新得很好,其他人警告的 DoEvents 怪异现象消失了。

  • 但是,刷新不会更新窗口。如果用户选择桌面上的另一个窗口,单击返回到您的窗口将没有任何效果,操作系统会将您的应用程序列为无响应。DoEvents() 所做的不仅仅是刷新,因为它通过消息传递系统与操作系统交互。 (3认同)