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

Kor*_*tak 23 c# graphics dispose image picturebox

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

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

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

Bitmap bmp = new Bitmap("file.png");
Run Code Online (Sandbox Code Playgroud)

所以我使用另一种方式加载位图.

public static Bitmap LoadBitmap(string path) {
    using (Bitmap original = new Bitmap(path))
    {
        return new Bitmap(original);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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; 
}
Run Code Online (Sandbox Code Playgroud)

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

因此,我有3个问题:

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

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

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

Oli*_*ver 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();            
    }
}
Run Code Online (Sandbox Code Playgroud)

更新

另一个受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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


小智 7

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

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

2:SolidBrush必须处理.处置有一般规则.- >"你实现的每一个实现dispose的对象必须手动处理,当对象 return/ref/out值时执行"

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

using (new objecttodispose){ ..... } 
Run Code Online (Sandbox Code Playgroud)

using声明将确保Dispose()在任何情况下的调用(例如例外).

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

  • 实际上,Bitmap和Image从不关闭底层流.你将对该文件进行永久锁定,直到GC关心收集浮动引用. (4认同)