简体   繁体   English

处理 Image/Bitmap 和 PictureBox 的正确方法

[英]Right way to dispose Image/Bitmap and PictureBox

I am trying to develop a Windows Mobile 6 (in WF/C#) application.我正在尝试开发 Windows Mobile 6(在 WF/C# 中)应用程序。 There is only one form and on the form there is only a PictureBox object.只有一个窗体,并且窗体上只有一个 PictureBox 对象。 On it I draw all desired controls or whatever I want.我在上面绘制了所有想要的控件或任何我想要的控件。

There are two things I am doing.我正在做两件事。 Drawing custom shapes and loading bitmaps from .png files.从 .png 文件绘制自定义形状和加载位图。

The next line locks the file when loading (which is an undesired scenario):下一行在加载时锁定文件(这是不希望的情况):

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

So I am using another way to load bitmap.所以我使用另一种方式来加载位图。

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

This is I guess much slower, but I don't know any better way to load an image, while quickly releasing the file lock.我猜这要慢得多,但我不知道有什么更好的方法来加载图像,同时快速释放文件锁。

Now, when drawing an image there is method that I use:现在,在绘制图像时,我使用了一种方法:

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; 
}

This however seems to be some kind of a memory leak.然而,这似乎是某种内存泄漏。 And if I keep doing it for too long, the application eventually crashes.如果我继续这样做太久,应用程序最终会崩溃。

Therefore, I have 3 questions:因此,我有3个问题:

1.) What is the better way for loading bitmaps from files without locking the file? 1.)从文件加载位图而不锁定文件的更好方法是什么?

2.) What objects needs to be manually disposed in the Draw() function (and in which order) so there's no memory leak and no ObjectDisposedException throwing? 2.)需要在 Draw() 函数中手动处理哪些对象(以及按什么顺序),以便没有内存泄漏和抛出 ObjectDisposedException?

3.) If pictureBox.Image is set to bmp, like in the last line of the code, would pictureBox.Image.Dispose() dispose only resources related to maintaining the pictureBox.Image or the underlying Bitmap set to it? 3.)如果pictureBox.Image 设置为bmp,就像在代码的最后一行一样,pictureBox.Image.Dispose() 是否只处理与维护pictureBox.Image 或设置为它的底层Bitmap 相关的资源?

I don't think there is a real memory leak .我不认为存在真正的内存泄漏 The problem is that you don't dispose the old bitmap, it is up to the GC to clean the stuff.问题是你不处理旧的位图,由 GC 来清理这些东西。 But there is no deterministic way to say when this will happen.但是没有确定性的方式来确定何时会发生这种情况。

So i think if you're going to loop through a lot of pictures, you'll see some memory increase and at some other point it will fall down or resist at one position.所以我认为如果你要循环浏览很多图片,你会看到一些记忆增加,而在其他时候它会掉下来或在一个位置抵抗。

I didn't test it, but maybe this will help a little to make it more deterministic:我没有测试它,但也许这会有助于使它更具确定性:

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();            
    }
}

Update更新

And another idea inspired by jack30lena:另一个灵感来自 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);
    }
}

The idea behind my second code sample is to get rid of the file handle and copy the file content into memory.我的第二个代码示例背后的想法是摆脱文件句柄并将文件内容复制到内存中。 Afterwards the Bitmap will taken ownership of the MemoryStream which will be disposed within my first sample by calling the oldImage.Dispose() .之后,位图将获得 MemoryStream 的所有权,它将通过调用oldImage.Dispose()在我的第一个示例中进行处理。

By using this approach there should never be more then two images in memory, thous leading only to OutOfMemoryExceptions by really big pictures or small amount of RAM.通过使用这种方法,内存中的图像永远不会超过两个,这只会导致真正的大图片或少量 RAM 导致 OutOfMemoryExceptions。

1: I dont know if it works in Windows Mobile but try this: 1:我不知道它是否适用于 Windows Mobile 但试试这个:

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

2: The SolidBrush must be disposed. 2:必须丢弃SolidBrush There is a general rule for dispose.处置有一个一般规则。 --> "every object, instanciated by you, that implements dispose must be disposed manually, exept when the object is a return/ref/out value" -->“每个对象,由你实例化,实现处置必须手动处置,除非对象返回/引用/输出值”

In this case it is better to use a using statement在这种情况下,最好使用using语句

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

The using statement will ensure the call of Dispose() in any case (exception for example). using语句将确保在任何情况下(例如例外Dispose()调用Dispose() )。

3: Dispose() will free the bitmap ressources. 3: Dispose()将释放位图资源。

You can get an Bitmap object from your controller and then assign it to image property of PictureBox.您可以从控制器中获取一个 Bitmap 对象,然后将其分配给 PictureBox 的 image 属性。 You should also dispose the current image of PictureBox to release the resource.您还应该处置PictureBox 的当前图像以释放资源。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM