24

我正在尝试开发 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 或设置为它的底层位图相关的资源?

4

3 回答 3

14

我认为没有真正的内存泄漏。问题是你没有处理旧的位图,由 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);
    }
}

我的第二个代码示例背后的想法是摆脱文件句柄并将文件内容复制到内存中。之后,位图将获得 MemoryStream 的所有权,这将通过调用oldImage.Dispose().

通过使用这种方法,内存中不应该有超过两个图像,只有非常大的图片或少量的 RAM 才会导致 OutOfMemoryExceptions。

于 2010-05-11T08:17:50.863 回答
7

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

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

2:SolidBrush必须丢弃。处置有一般规则。-->“你实例化的每个对象,实现 dispose 必须手动处理,当对象返回/引用/输出值时除外”

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

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

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

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

于 2010-05-11T07:36:54.620 回答
0

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

 var bmp = controller.GetBitmap();
 pictureBox1.Image.Dispose(); // this releases bitmap resources, required
 pictureBox1.Image = bmp;
于 2020-08-20T16:17:49.150 回答