与Application.DoEvents()和PictureBox混淆

时间:2014-07-02 17:25:40

标签: c# animation picturebox windows-forms-designer

我想制作一个小动画(简单的图片只是为了看它是如何工作的)并且必须做一天研究才能想出这个:

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()的目的是什么?有没有更好的方式来显示动画?

是的,我是初学者。

1 个答案:

答案 0 :(得分:1)

如何解决问题?

使用windows timer control并通过使用绘制图像的事件处理程序捕获计时器事件来渲染图像。无论如何,这是你应该这样做的方式。

DoEvents()有什么关系?

如果你想确切地知道你所拥有的DoEvents heckuva学习曲线。这非常复杂。这是我能想到的最简短的总结。

您可能会对此感到惊讶,但实际上您并没有编写Windows窗体应用程序。它已经为你写了。你唯一能写的就是事件处理程序和支持它们的函数。

所有Windows窗体应用程序都包含一个主线程,该主线程运行有时称为“message loop”或“消息泵”的操作。消息的示例是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); 
   }
}        

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

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

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

因此,您可以使用以下内容代替Sleep(1000)

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

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

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

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

DoEvents()是邪恶的

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

有关此主题的更多信息here