与Application.DoEvents()和PictureBox混淆

本文关键字:PictureBox 混淆 Application DoEvents | 更新日期: 2023-09-27 18:11:21

我想做一个小动画(简单的图片只是为了看看它是如何工作的),在能够想出这个之前,我不得不做了一天的研究:

private void pictureBox1_Click(object sender, EventArgs e)
    {
        for (int imgNum = 1; imgNum <= 5; imgNum++)
        {
            System.Threading.Thread.Sleep(300);
            pictureBox1.Image = (Image)(Properties.Resources.ResourceManager.GetObject(string.Format("Circle{0}", imgNum)));
        }
    }

然而,当我运行这个程序将显示第一张图像,等待1.2秒(睡眠时间),然后将继续显示最后一张图像。我记得做了一个使用Application.DoEvents()的练习,所以我试了一下,照片正确显示。

    private void pictureBox1_Click(object sender, EventArgs e)
    {
        for (int imgNum = 1; imgNum <= 5; imgNum++)
        {
            Application.DoEvents();
            System.Threading.Thread.Sleep(300);
            pictureBox1.Image = (Image)(Properties.Resources.ResourceManager.GetObject(string.Format("Circle{0}", imgNum)));
        }
    }

我的问题是Application.DoEvents()如何使图像显示。Application.DoEvents()的目的是什么?是否有更好的方式来显示动画?

是的,我是初学者。

与Application.DoEvents()和PictureBox混淆

如何解决这个问题?

使用windows计时器控件,并通过使用绘制图像的事件处理程序捕获计时器事件来呈现图像。无论如何,这是你应该做的。

DoEvents()是怎么回事?

如果你想确切地知道DoEvents是什么,你就需要一个巨大的学习曲线。这非常非常复杂。这是我能想到的最短的总结。

你可能会感到惊讶,但你并没有真正去写一个windows窗体应用程序。它已经为你写好了。你唯一能写的就是事件处理程序和支持它们的函数。

所有windows窗体应用程序都由一个主线程组成,这个主线程有时被称为"消息循环"或"消息泵"。消息的例子是WM_PAINT(命令应用程序自己绘制)或WM_COMMAND(这是鼠标点击或击键的结果)。消息泵就是一个巨大的循环,看起来就像这样:

MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
   if (bRet == -1)
   {
       // handle the error and possibly exit
   }
   else
   {
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
   }
}        

该循环以先进先出的方式从消息队列中读取消息,并调用处理消息,包括调用您自己的事件处理程序。

当事件处理程序正在运行时,事件循环未运行。这意味着你的Sleep()语句实际上阻止任何其他事件被处理,包括绘制应用程序或处理用户输入!

DoEvents命令指示事件处理程序将控制权交给消息泵,直到它为空,然后返回。因此,如果您不断调用DoEvents,应用程序可以在事件处理程序完成执行之前重新绘制自身或处理输入。

所以你可以用这样的代码代替Sleep(1000):

var waitUntil = System.DateTime.Now.AddSeconds(1);
while (System.DateTime.Now < waitUntil)
{
    Application.DoEvents();
}

…它将持续运行消息泵,直到时间间隔过去。

注意:如果你真的要这样做,我建议你使用一个高性能的计时器而不是System.DateTime。但是,您可能也不应该这样做,除非您正在做一些需要特殊处理的非常具体的事情。

相反,您应该使用非常常见的方法,即使用windows计时器控件,这正是用于此目的的。计时器控件将在时间过期时将WM_TIMER消息添加到消息队列中;这些将在适当的时候分派给计时器事件处理程序,同时让应用程序处理所有正在触发的其他消息。

DoEvents()是邪恶的

另外,请注意DoEvents()被认为是"邪恶的",因为它引入了线程问题。想象一下,如果事件处理程序在完成之前再次被调用!可能会有麻烦。

关于此主题的更多信息请点击此处