与 Application.DoEvents() 和 PictureBox 混淆 [重复]

Posted

技术标签:

【中文标题】与 Application.DoEvents() 和 PictureBox 混淆 [重复]【英文标题】:Confused with Application.DoEvents() and PictureBox [duplicate] 【发布时间】:2014-08-23 14:41:53 【问题描述】:

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

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("Circle0", 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("Circle0", imgNum)));
        
    

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

是的,我是初学者。

【问题讨论】:

使用带有 Refresh 的计时器,而不是 DoEvents 和 Sleep。见Use of Application.DoEvents() @LarsTech 我如何访问 ShowDialog()? 这与您的问题有什么关系?我在您的问题中没有看到 ShowDialog。 @LarsTech 您确实提供了一个指向明确提到 ShowDialog() 的答案的链接,实际上是多次。 【参考方案1】:

我该如何解决这个问题?

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

DoEvents() 是怎么回事?

如果您想确切地知道 DoEvents 是什么,那么您的学习曲线就很糟糕了。这是非常非常复杂的。这是我能想到的最简短的总结。

您可能会惊讶地发现这一点,但实际上您并不会编写 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

【讨论】:

以上是关于与 Application.DoEvents() 和 PictureBox 混淆 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

C#之Application.DoEvents()

什么相当于 WPF 应用程序中的 Application.DoEvents()

WPF 中的 Application.DoEvents() 在哪里?

Application.DoEvents() 在应用程序运行时不断调用?

等待 WebBrowser 完成加载时 Application.DoEvents() 出现问题

Application.DoEvents()的使用