为什么Refresh()不执行DoEvents()的功能?

Sab*_*ncu 0 .net c# multithreading invoke winforms

我试图理解Windows Forms中的某个长期概念:UI编程; 以下代码来自Chris Sells的Windows窗体编程书(第2版,2006):

void ShowProgress(string pi, int totalDigits, int digitsSoFar) {
  // Display progress in UI
  this.resultsTextBox.Text = pi;
  this.calcToolStripProgressBar.Maximum = totalDigits;
  this.calcToolStripProgressBar.Value = digitsSoFar;

  if( digitsSoFar == totalDigits ) {
    // Reset UI
    this.calcToolStripStatusLabel.Text = "Ready";
    this.calcToolStripProgressBar.Visible = false;
  }

  // Force UI update to reflect calculation progress
  this.Refresh();
}
Run Code Online (Sandbox Code Playgroud)

此方法是小样本应用程序的一部分,它具有另一个计算Pi的长期运行方法.每次计算一组数字时,都会调用ShowProgress()来更新UI.正如书中所解释的,这段代码是"错误"的处理方式,并且当应用程序最小化时会导致UI冻结,然后再次进入前台,导致系统要求应用程序重新绘制自己.

我不明白:由于this.Refresh()被重复调用,为什么它不处理任何等待注意的系统重绘事件?

还有一个后续问题:当我在this.Refresh()之后立即添加Application.DoEvents()时,冻结问题就会消失. 这是不必诉诸于Invoke/BeginInvoke等.任何评论?

Wik*_*hla 5

基本上,原因是Windows处理消息的方式 - 它在内部消息循环中以同步方式执行此操作.

关键是有一条消息触发了您的代码.例如按一下按钮.您的应用程序正在处理消息.在此处理程序中,您强制刷新,将另一个WM_PAINT放入消息队列中.当你的处理程序完成时,消息循环肯定会拾取并调度,从而重新绘制控件.但是你的代码还没有完成,实际上它会循环调用你的代码ShowProgress,导致WM_PAINT永远排队.

另一方面,DoEvents()导致消息循环的独立实例触发.它从发射的代码,这意味着调用堆栈看起来是这样的:

外部消息循环 - >您的代码 - >内部消息循环.

内部消息循环处理所有未决消息,包括WM_PAINT(因此控件被重新绘制),但它很危险 - 因为它将调度所有其他待处理消息,包括按钮点击,菜单点击或事件关闭您的应用程序,顶部的X -右上角.遗憾的是,没有简单的方法可以使循环仅处理WM_PAINT,这意味着调用DoEvents()会使您的应用程序暴露于涉及触发DoEvents的代码执行过程中意外用户活动的细微潜在问题.

  • 好吧,有**是一种专门获取WM_PAINT来分派的方法:表单的Update()方法.或刷新(),这里不必要.它没有发送*其他*消息,说服Windows线程没有抽取消息并在3秒后显示重影窗口.这是准确的,事实并非如此.有关DoEvents的更多信息,请访问:http://stackoverflow.com/questions/5181777/use-of-application-doevents/5183623#5183623 (2认同)