【问题标题】:Showing animated gif in Winforms without locking the file在 Winforms 中显示动画 gif 而不锁定文件
【发布时间】:2014-03-27 19:33:02
【问题描述】:

我正在尝试在我的 Winforms 应用程序中显示各种文件类型的图像(包括 动画 .gif 文件)。我还必须能够修改显示的文件。 (更改文件名,删除它们)。

问题是Picturebox locks the image file until the application is closed在使用正常方式时。

这意味着我不能这样做:

private void Form1_Load(object sender, EventArgs e)
    {

        PictureBox pic = new PictureBox();
        pic.Size = new Size(250, 250);
        pic.Image = Image.FromFile("someImage.gif");
        this.Controls.Add(pic);

                    //No use to call pic.Image = null or .Dispose of it
        File.Delete("someImage.gif"); //throws exception
    }

上面链接中的解决方法如下:

    private void Form1_Load2(object sender, EventArgs e)
    {
        PictureBox pic = new PictureBox();
        pic.Size = new Size(250, 250);
                    //using a FileStream
        var fs = new System.IO.FileStream("someImage.gif", System.IO.FileMode.Open, System.IO.FileAccess.Read);
        pic.Image = System.Drawing.Image.FromStream(fs);
        fs.Close();

        this.Controls.Add(pic);

        pic.MouseClick += pic_MouseClick;
    }

这适用于普通图像类型,但它不会加载动画 .gif,这对我很重要。尝试加载一个会使它看起来像this

我发现了一些关于它的其他主题(thisthis),但它们都是关于 WPF 并使用 BitmapImage。我已经搜索了如何在 Winforms 应用程序中使用 BitmapImage,但除了它应该以某种方式工作之外,没有发现任何其他东西。

我想继续使用 Winforms,因为我刚刚习惯了它,但这不是必需的。

总结一下:我需要一种方法来显示常见的图像类型(png、jpg、bmp 和动画 gif),同时仍然能够修改 HDD 上的文件。如果这意味着卸载->修改->重新加载文件,则可以。我更喜欢 Winforms,但其他框架也可以。

感谢您的帮助。

编辑:我尝试过的另一种方法

using (System.IO.FileStream fs = new System.IO.FileStream("E:\\Pics\\small.gif", System.IO.FileMode.Open, System.IO.FileAccess.Read))
        {
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            fs.CopyTo(ms);
            pic.Image = Image.FromStream(ms);
        } 

但显示与第二个示例相同的问题。 gif 无法加载。

【问题讨论】:

  • 将其作为内存流打开(从文件流中复制字节),然后将其加载到您可以提供给图片框的 gdi 对象中。这样就没有文件的链接了。
  • 好吧,当在 MemoryStream 上使用 GIF 时,有一个技巧要做:stackoverflow.com/questions/8763630/…
  • @woutervs 我用另一个代码 sn-p 编辑了帖子。这是你的意思吗?如果没有,您能详细说明吗?
  • 试试我的答案。在这种情况下,您甚至不需要 FileStream。
  • Hans Passant 的答案确实是将文件流复制到内存流的正确方法。 MrPaulch 的回答可能是 .net 4.5 的解决方案(我自己没有测试过。)

标签: c# .net winforms visual-studio-2010 window


【解决方案1】:

基本上,您必须在内存中制作图像文件的副本

Pre .Net 4.0 (2.0,3.0,3.5) 您必须创建一个 FileStream 并将其复制到 MemoryStream 并倒带,如另一个答案所示。

由于.Net 4.0 (4.0,4.5,...) Image.FromFile 支持动画 GIF


如果您使用.Net 4.0稍后以下方法就足够了:

使用System.IOSystem.DrawingSystem.Drawing.Imaging

 private void Form1_Load(object sender, EventArgs e)
    {
        string szTarget = "C:\\someImage.gif";

        PictureBox pic = new PictureBox();
        pic.Dock = DockStyle.Fill;

        Image img = Image.FromFile(szTarget);   // Load image fromFile into Image object

        MemoryStream mstr = new MemoryStream(); // Create a new MemoryStream
        img.Save(mstr, ImageFormat.Gif);        // Save Image to MemoryStream from Image object

        pic.Image = Image.FromStream(mstr); // Load Image from MemoryStream into PictureBox
        this.Controls.Add(pic); 

        img.Dispose(); // Dispose original Image object (fromFile)
        // after this you should be able to delete/manipulate the file

        File.Delete(szTarget);
    }

【讨论】:

  • 不是一个好主意,Image.Save() 不支持保存动画 gif。你测试过这个吗?
  • 是在上述环境中测试过。 GIF 已正确显示和动画。我也很惊讶,因为我认为我必须使用 FileStream 并将其复制到 MemoryStream,但显然 Image 至少在 .Net 4.5 中支持动画 GIF
【解决方案2】:

使用 MemoryStream 确实是避免文件锁定的正确方法。顺便说一句,这是一个强大的优化,锁是由 Image 类用来将像素数据保留在页面文件之外的内存映射文件创建的。当位图很大时,这很重要。希望不是在动画 gif 上 :)

您的代码 sn-p 中的一个小错误,您忘记将流重置回数据的开头。修复:

 using (var fs = new System.IO.FileStream(...)) {
     var ms = new System.IO.MemoryStream();
     fs.CopyTo(ms);
     ms.Position = 0;                               // <=== here
     if (pic.Image != null) pic.Image.Dispose(); 
     pic.Image = Image.FromStream(ms);
 } 

如果需要说明:处置内存流。这导致以后很难诊断随机崩溃,像素数据被延迟读取。

【讨论】:

  • 另一种重置流的方法 => ms.seek(seekposition.begin, 0)(我记忆中的代码可能不完全正确;))
  • 当我再次回到我的工作站时,我会在我的回答中包含这个内容。
  • 两个答案都有效,但这个似乎更普遍。因为它适用于所有图像类型而无需修改。所以我会接受这个。谢谢你的帮助。
  • 如果这是一个经常发生的操作,您需要跟踪MemoryStream,以便您可以在处理图像后处理它。
  • 废话,MemoryStream 不使用任何可以从处置中受益的非托管资源。它具有 Dispose 方法的唯一原因是因为它从 Stream 继承了该方法,它不能取消继承该方法。当然它不会覆盖 Dispose(bool),没什么可做的。
猜你喜欢
  • 2016-08-23
  • 1970-01-01
  • 2012-09-15
  • 1970-01-01
  • 1970-01-01
  • 2011-02-25
  • 2012-09-15
  • 2013-03-14
  • 1970-01-01
相关资源
最近更新 更多