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
方法替换image属性(语义是,一旦图像被传递到SetImageWithOwnership
,该框将被期望“拥有”它,并在框为PictureOwningBox
d或向该框提供不同的图像时对其进行处理)dispose
- 将事件处理程序附加到
,以处理盒子被销毁或为其分配不同图像的场景PictureBox
- 如果有任何代码会导致处理
,或者加载了不同的图像,也可以处理分配给它的图片盒
图像
虽然在某些情况下,调用
GC.Collect
并让垃圾收集器处理事情是合适的,但这种方法通常是不可靠的。事实上,我在op中更改了代码,与您建议的不同。似乎使用()
也允许这种行为。事实上,从我所读到的内容中,我了解到编译期间using()
的读法类似于tyr catch finally
block withDispose
,它是从finally
子句调用的,在我看来与:if(MyImage!=null)没有太大区别{MyImage.Dispose();}
谢谢,我很惊讶这个问题没有标准的解决方案。对于许多使用PictureBox的人来说,这似乎是一个问题。