关于Application.DoEvents()

今天在研究网页快照,其中涉及到了实例化 WebBrowser ActiveX 控件,其中有一段是WebBrowser加载网页处理的代码:

WebBrowser m_WebBrowser = new WebBrowser();
			m_WebBrowser.ScrollBarsEnabled = false;
			m_WebBrowser.Navigate(m_Url);
			m_WebBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
			while (m_WebBrowser.ReadyState != WebBrowserReadyState.Complete)
				Application.DoEvents();
			m_WebBrowser.Dispose();

里面有一个while循环,我理解为:当未执行完成的时候不断的重新绘制窗体,不知道对不对,如果不对麻烦哪位给我解释一下,谢谢;

重新绘制窗体的代码为Application.DoEvents()  我看了MSDN,不太理解,就上网查了一下,一篇文章讲的很透彻,在这里转一下,以备查找:

原文地址

MSDN中的备注是:

当运行 Windows 窗体时,它将创建新窗体,然后该窗体等待处理事件。该窗体在每次处理事件时,均将处理与该事件关联的所有代码。所有其他事件在队列中等待。在代码处理事件时,应用程序并不响应。例如,当将另一窗口拖到该窗口前面时,该窗口不重新绘制。

如果在代码中调用 DoEvents,则您的应用程序可以处理其他事件。例如,如果您有向 ListBox 添加数据的窗体,并将 DoEvents 添加到代码中,那么当将另一窗口拖到您的窗体上时,该窗体将重新绘制。如果从代码中移除 DoEvents,那么在按钮的单击事件处理程序执行结束以前,您的窗体不会重新绘制。

刚开始接触这段话的时候,会读不懂。所以我就去找了篇文章,文章中通过一个循环输出文本的例子解释了通过这段代码可以给人带来很好的用户体验( 不加的话程序只会显示输出循环最后的结果,但是加了以后会实时的输出),从而消除了认为进程死掉的情况。他还提到了用这个的缺点是影响了进程的效率,并通过了一个测验证实他的观点。

看了他的博客,我也就仿照他的说明写了个代码进行了测验,第一个代码是同他说的一样,加不加代码的效果是不同的。但是第二个,因为用一个计时器来计时。我因为不是很懂计时器,我就写了个类似计时器的代码,原理感觉上和他的差不多。

int num = 0;
		private void button1_Click(object sender, EventArgs e) {
			this.timer1.Start();
			this.timer1.Interval = 1000;
			this.timer1.Tick += new EventHandler(timer1_Tick);
			for (int q = 0; q < 10000; q++) {
				textBox1.Text = q.ToString();
				Application.DoEvents();
				if (q == 9999) {
					this.timer1.Stop();
				}
			}
		}

		private void timer1_Tick(object sender, EventArgs e) {
			num++;
			label2.Text = num.ToString();
		}

		private void button2_Click(object sender, EventArgs e) {
			this.timer1.Start();

			this.timer1.Interval = 1000;

			this.timer1.Tick += new EventHandler(timer1_Tick);

			for (int q = 0; q < 100000; q++) {

				textBox2.Text = q.ToString();

				if (q == 99999) {

					this.timer1.Stop();

				}

			}

			label2.Text = num.ToString();
		}

原理是:用Timer组件,写了一个Tick事件并设置了间隔时间,在事件内用了一个counter 来计数,再通过计数的结果已经响应的时间间隔来计算时间。再运行加了DoEvents代码的运行的很好,和我想的一样。

button2_Click
运行后发现,textBox2. 可以得到99999 就是结果的值,但是 label2 却一直是零,也就相当于没有触发Tick 事件,这就把给搞昏了,我明明是设定了Timer Start 而且也在循环之前调用的Tick  事件,这是为什么呢?我又试着将代码里的stop  给注释掉,再运行,在循环结束后可以运行Tick  事件了。从这个结果来看,开始以为Tick 本来是可以运行的。因为Tick 的触发是 当指定的计时器间隔已过去而且计时器处于启用状态时发生。 那么有可能时间间隔太长,导致时间间隔没解释,也就是 Tick 来不及触发。为了验证这个是否正确,我将时间间隔调小,但是结果还是那样,而且到后来我直接将 Tick 的事件触发代码带到了循环中,可结果还是那样。到此,我想也许是存在优先级的概念,是否循环这个进程的优先级比 Tick 的优先级高。之前在循环体内设置的到 99999 后的 Stop Timer ,直接导致 Tick 刚要执行就被强行关掉了。因为 MSDN 上也查不到这方面的原因,我就此当做是结论了。

从这个测验后再回头想DoEvents的功能,我就想DoEvents就好比实现了进程的同步。在不加的时候,因为优先级的问题,程序会执行主进程的代码,再执行Tick的代码,而加了以后就可以同步执行。

从这个测验后再回头想DoEvents的功能,我就想DoEvents就好比实现了进程的同步。在不加的时候,因为优先级的问题,程序会执行主进程的代码,再执行Tick的代码,而加了以后就可以同步执行.

关于DoEvents事件的使用

记得第一次使用Application.DoEvents()是为了在加载大量数据时能够有一个数据加载的提示,不至于系统出现假死的现象,当时也没有深入的去研究他的原理是怎样的,结果在很多地方都用上了Application.DoEvents(),今天看到了关于这方面的一些文章,知道我以前有些用法是不当的,有些地方需要慎用Application.DoEvents()。
首先我们先看看在循环比较大的程序中,它的作用还是不错的,起到了一个实时响应的效果,例如:

for  ( int  q  =   0 ; q  <   1000000 ; q ++ )
            
{
                textBox1.Text 
= q.ToString();
                Application.DoEvents();//实时响应文本框中的值
            }
如果没有加上 DoEvents的话,由于循环时间会比较久就会出现假死的状态,而且程序不能处理其他的事件。而如果加上DoEvents的话就会对文本框的值实时响应,给用户带来较好的用户体验,可是DoEvents也带来了效率上的问题,处理同样的一个事件调用了DoEvents后效率降低了好几倍,这也是为什么要慎用的原因了。下面是我做的一个测试:
         private   void  button1_Click( object  sender, EventArgs e)
        
{
            expendTime.start();
            
for (int q = 0; q < 100000; q++)
            
{
                textBox1.Text 
= q.ToString();
                Application.DoEvents();
            }

            label2.Text 
= expendTime.ComputerTime();//计算耗时
        }


        
private   void  button2_Click( object  sender, EventArgs e)
        
{
            expendTime.start();
            
for (int q = 0; q < 100000; q++)
            
{
                textBox2.Text 
= q.ToString();
            }

            label3.Text 
= expendTime.ComputerTime();//计算耗时
        }

执行耗时对比:
从较大数据的循环中可以看出效率是很低的,所以如果能不调用DoEvents就尽量不用。也可以通过别的方法来处理的,例如多线程异步调用等。
MSDN中的定义:
当运行   Windows   窗体时,它将创建新窗体,然后该窗体等待处理事件。该窗体在每次处理事件时,均将处理与该事件关联的所有代码。所有其他事件在队列中等待。在代码处理事件时,应用程序并不响应。例如,当将另一窗口拖到该窗口前面时,该窗口不重新绘制。如果在代码中调用   DoEvents,则您的应用程序可以处理其他事件。例如,如果您有向   ListBox   添加数据的窗体,并将   DoEvents   添加到代码中,那么当将另一窗口拖到您的窗体上时,该窗体将重新绘制。如果从代码中移除   DoEvents,那么在按钮的单击事件处理程序执行结束以前,您的窗体不会重新绘制。   
 通常,您在循环中使用该方法来处理消息。
具体可 参考这里



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值