正确的方式来配置Image / Bitmap和PictureBox

时间:2010-05-11 07:21:27

标签: c# image graphics dispose picturebox

我正在尝试开发Windows Mobile 6(在WF / C#中)应用程序。只有一个表单,表单上只有一个PictureBox对象。在它上面我绘制所有想要的控件或我想要的任何东西。

我正在做两件事。绘制自定义形状并从.png文件加载位图。

下一行在加载时锁定文件(这是一种不受欢迎的情况):

Bitmap bmp = new Bitmap("file.png");

所以我正在使用另一种方法来加载位图。

public static Bitmap LoadBitmap(string path) {
    using (Bitmap original = new Bitmap(path))
    {
        return new Bitmap(original);
    }
}

我猜这要慢得多,但我不知道有什么更好的方法来加载图像,同时快速释放文件锁。

现在,在绘制图像时,我使用的方法是:

public void Draw() {
    Bitmap bmp = new Bitmap(240,320);
    Graphics g = Graphics.FromImage(bmp);

    // draw something with Graphics here.
    g.Clear(Color.Black);
    g.DrawImage(Images.CloseIcon, 16, 48);
    g.DrawImage(Images.RefreshIcon, 46, 48);
    g.FillRectangle(new SolidBrush(Color.Black), 0, 100, 240, 103);

    pictureBox.Image = bmp; 
}
然而,这似乎是某种内存泄漏。如果我继续这么做,应用程序最终会崩溃。

因此,我有3个问题:

1。)在不锁定文件的情况下从文件加载位图的更好方法是什么?

2。)需要在Draw()函数中手动处理哪些对象(以及以哪种顺序),因此没有内存泄漏且没有抛出ObjectDisposedException?

3。)如果pictureBox.Image设置为bmp,就像代码的最后一行一样,pictureBox.Image.Dispose()只会处理与维护pictureBox.Image相关的资源或基础Bitmap设置为它?

3 个答案:

答案 0 :(得分:13)

我认为没有真正的内存泄漏。问题是你没有处理旧的位图,由GC来清理这些东西。但是没有确定的方法可以说何时会发生这种情况。

所以我认为如果你要循环浏览很多图片,你会看到一些内存增加,而在某些其他方面它会在一个位置下降或抵抗。

我没有对它进行测试,但也许这有助于使其更具确定性:

public void Draw() {
    Bitmap bmp = new Bitmap(240,320);
    using(var g = Graphics.FromImage(bmp))
    using(var solidBrush = SolidBrush(Color.Black))
    {
        // draw something with Graphics here.
        g.Clear(Color.Black);
        g.DrawImage(Images.CloseIcon, 16, 48);
        g.DrawImage(Images.RefreshIcon, 46, 48);
        g.FillRectangle(solidBrush, 0, 100, 240, 103);

        //Backup old image in pictureBox
        var oldImage = pictureBox.Image;
        pictureBox.Image = bmp; 
        //Release resources from old image
        if(oldImage != null)
            ((IDisposable)oldImage).Dispose();            
    }
}

更新

另一个受jack30lena启发的想法:

public static Bitmap LoadBitmap(string path)
{
    //Open file in read only mode
    using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
    //Get a binary reader for the file stream
    using (BinaryReader reader = new BinaryReader(stream))
    {
        //copy the content of the file into a memory stream
        var memoryStream = new MemoryStream(reader.ReadBytes((int)stream.Length));
        //make a new Bitmap object the owner of the MemoryStream
        return new Bitmap(memoryStream);
    }
}

我的第二个代码示例背后的想法是摆脱文件句柄并将文件内容复制到内存中。之后,Bitmap将取得MemoryStream的所有权,并通过调用oldImage.Dispose()将其置于我的第一个样本中。

通过使用这种方法,内存中永远不会有两个以上的图像,只能通过真正的大图片或少量的RAM导致OutOfMemoryExceptions。

答案 1 :(得分:7)

1: 我不知道它是否适用于Windows Mobile,但试试这个:

FileStream bitmapFile = new FileStream("mybitmap.bmp", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Image loaded = new Bitmap(bitmapFile);

2:必须处理SolidBrush。处置有一般规则。 - > “你实施的每一个实施处置的物品都必须手动处理,当物体 一个返回/参考/退出价值时执行”

在这种情况下,最好使用using语句

using (new objecttodispose){ ..... } 

using语句将确保在任何情况下调用Dispose()(例如,例外)。

3:Dispose()将释放位图资源。

答案 2 :(得分:0)

您可以从控制器获取Bitmap对象,然后将其分配给PictureBox的image属性。您还应该处置PictureBox的当前图像以释放资源。

 var bmp = controller.GetBitmap();
 pictureBox1.Image.Dispose(); // this releases bitmap resources, required
 pictureBox1.Image = bmp;