Правильный способ распоряжаться Image / Bitmap и PictureBox - PullRequest
22 голосов
/ 11 мая 2010

Я пытаюсь разработать приложение для 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?

Ответы [ 2 ]

13 голосов
/ 11 мая 2010

Я не думаю, что есть настоящая утечка памяти . Проблема в том, что вы не утилизируете старое растровое изображение, это дело 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().

При использовании этого подхода в памяти никогда не должно быть более двух изображений, что приводит только к исключениям OutOfMemoryException из-за больших изображений или небольшого объема ОЗУ.

7 голосов
/ 11 мая 2010

1: Я не знаю, работает ли он в Windows Mobile, но попробуйте это:

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

2: SolidBrush должен быть утилизирован. Существует общее правило для утилизации. -> "каждый объект, созданный вами, который реализует утилизацию, должен быть удален вручную, кроме случаев, когда объект равен возвращаемому / ref / out значению"

В этом случае лучше использовать оператор using

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

Оператор using обеспечит вызов Dispose() в любом случае (исключение, например).

3: Dispose() освободит ресурсы растрового изображения.

...