C# .NET PictureBox-如何确保资源已释放

C# .NET PictureBox-如何确保资源已释放,c#,dispose,picturebox,C#,Dispose,Picturebox,我在用户控件中有一个OpenFileDialog和PictureBox。为了更好地理解这个问题,我将用几句话解释这个用户控件是如何工作的。用户可以选择要为表单打开的图像。此图像的名称保存在数据库中,图像的文件复制到默认位置。如果数据库中保存了一些图像,则在加载带有picturebox控件的表单时,会将其加载到picturebox中。如果用户选择另一个图像并希望使用新图像保存表单,我有一种方法,可以从默认位置删除旧图像文件,这就是问题发生的地方 当我加载图像并尝试保存新图像时,有时(事实上非常罕见

我在用户控件中有一个
OpenFileDialog
PictureBox
。为了更好地理解这个问题,我将用几句话解释这个用户控件是如何工作的。用户可以选择要为表单打开的图像。此图像的名称保存在数据库中,图像的文件复制到默认位置。如果数据库中保存了一些图像,则在加载带有picturebox控件的表单时,会将其加载到picturebox中。如果用户选择另一个图像并希望使用新图像保存表单,我有一种方法,可以从默认位置删除旧图像文件,这就是问题发生的地方

当我加载图像并尝试保存新图像时,有时(事实上非常罕见)会出现一个错误,
另一个进程正在使用该资源。
如果需要,我可以粘贴准确的错误。我认为这个问题是由picturebox及其处理图像的方式引起的

这是我的密码:

if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    if (MyImage != null)
                    {
                        MyImage.Dispose();

                    }
                    selectedFile = openFileDialog1.FileName;
                    selectedFileName = openFileDialog1.SafeFileName;

                    MyImage = new Bitmap(openFileDialog1.FileName);
                    pictureBox1.Image = (Image)MyImage;

                    int imageWidth = pictureBox1.Image.Width;
                    int picBoxWidth = pictureBox1.Width;

                    if (imageWidth != 0 && picBoxWidth > imageWidth)
                    {
                        pictureBox1.Width = imageWidth;
                    }
                    else
                    {
                        pictureBox1.Width = defaultPicBoxWidth;
                    }
                }
                catch (Exception ex)
                {
                    logger.Error(ex.ToString());
                    MessageBox.Show("Error loading image!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
以及我的删除方法:

public void DeleteImage(AppConfig imageInfo, string imageName)
        {
            string imgPath = imageInfo.ConfigValue.ToString();
            try
            {
                File.Delete(imgPath + "\\" + imageName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
我想:

if (MyImage != null)
                        {
                            MyImage.Dispose();

                        }
将处理此问题,但有时仍会发生。因为不是每次都是这样,所以处理它更为关键,因为在某个时候,我可能会认为我已经解决了它,但事实上只是幸运了一段时间。

试试看:

                using(Bitmap MyImage = new Bitmap(openFileDialog1.FileName))
                {
                  pictureBox1.Image = (Image)MyImage;

                  int imageWidth = pictureBox1.Image.Width;
                  int picBoxWidth = pictureBox1.Width;

                  if (imageWidth != 0 && picBoxWidth > imageWidth)
                  {
                      pictureBox1.Width = imageWidth;
                  }
                  else
                  {
                      pictureBox1.Width = defaultPicBoxWidth;
                  }
                }

我以前也遇到过类似的问题,我找到了一种方法来确保资源被释放,即使在
Dispose()
(实际上只标记垃圾收集器要删除的对象)之后也是如此,那就是使用
GC.Collect()
。我确信有一种更干净的方法来处理资源处理,但是运行
GC.Collect()
所需的时间不应该妨碍您的程序

    MyImage = new Bitmap(openFileDialog1.FileName);
    pictureBox1.Image = (Image)MyImage;
是的,该代码会锁定文件。锁由GDI+创建的内存映射文件对象生成,该对象可以有效地将文件的像素数据映射到内存中,而无需在分页文件中分配空间。只要图像显示在图片框中且未被释放,您将无法删除文件,锁定将阻止删除。在删除文件之前,必须先处理图像并将图像属性设置回null

通过制作图像的内存中副本,可以防止文件被锁定:

    using (var temp = new Bitmap(openFileDialog1.FileName)) {
        pictureBox1.Image = new Bitmap(temp);
    }

当然,如果图像较大,则需要避免使用这种方法。请注意,另一个进程实际上可能对文件具有类似的锁定。你对此无能为力。

PictureBox
这样的东西的一个主要困难是,因为
PictureBox
无法知道它是否是图像的唯一用户,因此无法知道它是否应该在不再需要图像时处理该图像

因此,任何拥有图片框的代码也必须拥有与之相关联的图像的所有权。我可以提出三种方法:

  • 创建一个从
    PictureBox
    派生的控件,该控件将其自身记录为对给定给它的任何图像的所有权。这样的控件可能会用
    SetImageWithOwnership
    方法替换image属性(语义是,一旦图像被传递到
    PictureOwningBox
    ,该框将被期望“拥有”它,并在框为
    dispose
    d或向该框提供不同的图像时对其进行处理)

  • 将事件处理程序附加到
    PictureBox
    ,以处理盒子被销毁或为其分配不同图像的场景

  • 如果有任何代码会导致处理
    图片盒
    ,或者加载了不同的图像,也可以处理分配给它的
    图像


虽然在某些情况下,调用
GC.Collect
并让垃圾收集器处理事情是合适的,但这种方法通常是不可靠的。

事实上,我在op中更改了代码,与您建议的不同。似乎
使用()
也允许这种行为。事实上,从我所读到的内容中,我了解到编译期间
using()
的读法类似于
tyr catch finally
block with
Dispose
,它是从
finally
子句调用的,在我看来与:
if(MyImage!=null)没有太大区别{MyImage.Dispose();}
谢谢,我很惊讶这个问题没有标准的解决方案。对于许多使用
PictureBox的人来说,这似乎是一个问题。