循环浏览一组图像并使用DrawImageUnscale逐个显示它们

本文关键字:DrawImageUnscale 显示 浏览 图像 一组 循环 | 更新日期: 2023-09-27 18:21:06

我正在开发一个Windows窗体应用程序,该应用程序由屏幕上的单个图像和加载时名为animate的按钮组成。当单击该按钮时,它应该循环浏览10个图像(保存在文件夹中为"1.png"、"2.png"…"10.png"),并依次显示它们,从而创建动画效果。

但是,当我单击动画按钮时,它不会执行任何操作。但是,如果我在单击animate按钮后单击屏幕,它会显示保存在目录中的最后一张图像("10.png")。

这是我为Form1_Paint()方法编写的代码:这是在加载表单时应用的。

pictureBit是原始图像的名称)

private void Form1_Paint(object sender, PaintEventArgs e)
{
    Graphics windowG = e.Graphics;
    if (animate)
    {
        for (int i = 1; i <= 10; i++)
        {
            Bitmap images = (Bitmap)Image.FromFile(i+".png");
            windowG.DrawImageUnscaled(images, 0, 0);
        }
    }
    else
    {
        windowG.DrawImageUnscaled(pictureBit, 0, 0);
    }
}

当点击按钮时,它会将布尔值设置为true:

private void start_animate(object sender, EventArgs e)
{
    animate = true;
} 

解决这个问题的最佳方法是什么?

循环浏览一组图像并使用DrawImageUnscale逐个显示它们

代码中的问题是在Paint事件处理程序中循环。往好了说,这会导致程序的其余部分在图像设置动画时冻结,往坏了说(在您的场景中),您会发现,由于Windows处理屏幕更新的方式,屏幕图像只有在处理程序返回后才会更新,根本无法看到动画。

对于像您正在做的这样的简单动画,最简单的方法是更新计时器上的Bitmap并使表单无效,并且让Paint事件处理程序只绘制一次。例如:

private Bitmap _currentImage;
private void Form1_Paint(object sender, PaintEventArgs e)
{            
    if (animate)
    {                            
        e.Graphics.DrawImageUnscaled(_currentImage, 0, 0);
    }
    else
    {
        e.Graphics.DrawImageUnscaled(pictureBit, 0, 0);
    }

}

private async void start_animate(object sender, EventArgs e)
{
    animate = true;
    int imageIndex = 1;
    while (animate)
    {
        _currentImage = (Bitmap)Image.FromFile(imageIndex  + ".png");
        Invalidate();
        await Task.Delay(100); // wait 100 milliseconds
        imageIndex++;
        if (imageIndex > 10)
        {
            imageIndex = 1;
        }
    }
}

在这里使用asyncawait可以使代码看起来像一个正常的循环,但它允许方法返回,并允许线程在每次失效之间(即执行await Task.Delay(100)时)继续处理。

请注意,上面的重点是尝试以固定的间隔为图像设置动画。无法保证表单会像更新图像一样频繁地重新绘制。通常情况下,对于动画来说,这仍然是可取的(即,您可能会错过一些帧,但整个动画会以预期的速率发生)。但是,如果需要,您可以协调动画循环和Paint事件处理程序,这样动画循环只有在收到事件处理程序已更新表单的信号后才能继续到下一帧。

还要注意,上面的"固定间隔"没有考虑到处理文件I/O和索引的任何开销。通过使用Stopwatch来跟踪开销需要多长时间,并从环路中的延迟中减去开销,可以实现更精确的定时。由于您的原始代码没有尝试在特定的时间间隔安排动画,我认为上面的代码示例不需要比现在更高的精度。:)


警告:由于您没有提供一个可靠地再现问题的良好的最小完整代码示例,因此从头创建一个完整的程序来验证所提出的解决方案是不现实的。以上只是浏览器代码;我已经编辑过一次,以修复我在查看它时发现的语法和逻辑问题,但我不能保证不会有更多。:)我认为,基本的想法已经足够让你适应自己的需求。

是的,这不会像你想的那样奏效。直到方法返回后的一段时间,Paint的结果才会转发到设备(屏幕);这允许窗口系统进行一些优化。因此,在将任何内容发送到屏幕之前,您的Paint方法基本上会绘制所有十个图像

在单击表单之前,您不会看到任何东西的原因是,只有当表单认为需要重新绘制时,才会调用Paint。简单地更改控件的内容可能不会导致这种情况发生。看看Control::Invalidate()方法。

您可能会考虑使用System::Windows::Forms::Timer定期更新映像并使控件无效(如果需要)。这将所有内容保留在Forms线程上,还允许您对图像的更改速率进行一些控制。点击按钮可以启动计时器。

我还建议提前将所有图像加载到内存中,而不是在您打算显示的时候从文件中读取每个图像。

相关文章:
  • 没有找到相关文章