【问题标题】:The dispose of an OpenFileDialog in C#?在 C# 中处理 OpenFileDialog?
【发布时间】:2013-08-11 12:56:43
【问题描述】:

我搜索了整个 StackOverflow,但找不到以下问题的答案:

当我使用OpenFileDialog 时,我打开的文件会被阻止在我的程序之外使用,直到我关闭我的程序。因此,如果我打开一张图片,我将无法再替换我的Windows Explorer 中的那张图片。

我认为这是处理我的 OpenFileDialog 的问题,但我不确定如何解决它...

我的代码:

using (OpenFileDialog ofd = new OpenFileDialog())
{
    ofd.Title = "Open Image";
    ofd.Filter = "PNG Image(*.png|*.png" +
                 "|GIF Image(*.gif|*.gif" +
                 "|Bitmap Image(*.bmp|*.bmp" +
                 "|JPEG Compressed Image (*.jpg|*.jpg";

    if (ofd.ShowDialog() == DialogResult.OK)
    {
        pictureBox1.Image = new Bitmap(ofd.FileName);
    }
}

我认为using 块可以解决这个问题,但是不......它仍然被程序使用。我想将图像加载到图片框中,然后我希望图像再次可用(这样我就可以重命名、替换它等...)。

【问题讨论】:

    标签: c# picturebox dispose using openfiledialog


    【解决方案1】:

    这与 OpenFileDialog 无关。不可能,因为对话框实际上并没有打开文件。它只允许用户选择要打开的文件,然后将该路径返回给您,以便您可以编写打开文件的代码。此外,您通过使用 using 语句正确处理了 OpenFileDialog。

    这里的问题来自您实际打开文件—ofd.FileName— 作为位图。当您使用the Bitmap constructor overload that accepts a path string 时,磁盘上包含图像的文件将保持锁定状态,直到释放 Bitmap 对象。文档如是说:

    在处理位图之前,文件保持锁定状态。

    因为您将位图分配给pictureBox1.Image,所以在释放pictureBox1 之前不会释放位图对象。因此,您在磁盘上的图像文件将保持锁定状态。

    如果您想解锁文件,您需要制作位图副本并处理原始文件,或者清除 PictureBox 并在完成后处理其先前的图像。

    据我了解您的问题,听起来您希望能够更改磁盘上的图像文件,同时继续在图片框中显示图像。如果是这种情况,您将需要制作副本。使用带有 Image 的构造函数重载来执行此操作,如下所示:

    if (ofd.ShowDialog() == DialogResult.OK)
    {
        // Load the image the user selected
        using (Image img = Image.FromFile(ofd.FileName))
        {
            // Create a copy of it
            Bitmap bmpCopy = new Bitmap(img);
    
            // Clear out the bitmap currently in the picture box,
            // if there is one.
            if (pictureBox1.Image != null)
            {
                pictureBox1.Image.Dispose();
            }
    
            // Assign the copy of the bitmap to the picture box.
            pictureBox1.Image = bmpCopy;
        }
    }
    

    【讨论】:

    • 我可以确认这可能是问题所在。我过去被它咬过。您可以考虑读取文件字节并从MemoryStream 创建Bitmap 的一种选择。
    • @Chris 你确定流不会锁定文件吗?请记住,您必须在从它创建的任何位图对象的生命周期内保持流打开。
    • 你得到了所有的选票,你确实需要修正你的答案。 Bitmap.Clone() 创建一个浅拷贝,它仍然使用原始位图的像素数据。所以不会释放文件上的锁。随意使用我的答案,我会删除我的。
    • @Hans 哎呀,谢谢。我记错了 Clone 创建了位图的深层副本。正在修复...
    • 您的代码不能复制粘贴不是问题所在。我首先尝试了这个,制作一个克隆,处理旧副本并使用克隆。但我最终得到了同样的错误,所以我回到这里并检查了第二个答案,这是 xanatos 的答案。非常感谢您已经付出了努力,我认为我在实施您的意思时做错了!真的非常感谢!
    【解决方案2】:

    正如 Chris 所写,尝试类似:

    pictureBox1.Image = Image.FromStream(new MemoryStream(File.ReadAllBytes(old.FileName)));
    

    它读取所有带有File.ReadAllBytes 的文件,将其放入MemoryStream 并将MemoryStream 传递给Image 静态初始化程序。

    相当于:

    byte[] bytes = File.ReadAllBytes(old.FileName);
    MemoryStream ms = new MemoryStream(bytes);
    pictureBox1.Image = Image.FromStream(ms);
    

    您不得丢弃MemoryStream!如果/当 Image 将被处置,MemoryStream 的终结器将启动(如果您没有其他对 ms 的引用)并且 MemoryStream 将被处置(请注意,这不是什么这将立即发生......这是在 GC 运行时会发生的事情)

    【讨论】:

    • 非常感谢。我从字面上复制了您的代码规则,它立即生效,非常感谢!
    • 哎呀,猜猜我的回答出了什么问题:没有可复制粘贴的代码。哦,好吧,经常发生在我身上。我仍然认为克隆位图比看起来泄漏 MemoryStream 更好。这永远不会通过代码审查,需要大量评论;就像你提到的那样,这很不直观。
    • @CodyGray 我给了你一个 +1,但关键是克里斯在评论中给出了最后一英里(使用 MemoryStream 类)。您的回答非常“理论化”。
    • 对,重点是我不同意那个解决方案是最优的。如果我认为这是个好主意,我会在我的回答中写下它。
    • 我只想提一下,如果我没记错的话,所有不使用 ImageConverter 的技术都有一个小问题,因为否则位深度总是设置为 32 位,dpi 设置为 dpi的屏幕。这通常无关紧要,但如果您想保留源图像的位深度和 dpi,那就不是很好了。但我不是 100% 确定...
    【解决方案3】:

    我发现最好的技术是使用 File.ReadAllBytes()(打开和关闭文件)将文件读入字节数组,然后使用 ImageConverter 将字节数组转换为图像。例如,请参见此处:https://stackoverflow.com/a/16576471/253938

    编辑: 引用我之前的帖子:“我尝试过的其他一些技术并不是最佳的,因为它们改变了像素的位深度(24 位与 32 位)或忽略了图像的分辨率 (dpi) 。”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-09
      • 2013-03-19
      • 1970-01-01
      • 2011-07-19
      • 1970-01-01
      相关资源
      最近更新 更多