Правильный способ утилиты 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 или базовый битмап установлен на него?
Ответы
Ответ 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 по действительно большим изображениям или небольшому объему ОЗУ.