Подтвердить что ты не робот

Правильный способ утилиты Image/Bitmap и PictureBox

Я пытаюсь разработать приложение Windows Mobile 6 (в WF/С#). Существует только одна форма, и на форме есть только объект 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 или базовый битмап установлен на него?

4b9b3361

Ответ 1

1: Я не знаю, работает ли он в WM, но попробуйте следующее:

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

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

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

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

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

3: Dispose() освободит ресурсы битмапа.

Ответ 2

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

Update

И еще одна идея, вдохновленная 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().

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