J'essaie de développer une application Windows Mobile 6 (en WF/C #). Il n'y a qu'un seul formulaire et sur le formulaire, il n'y a qu'un objet PictureBox. J'y dessine tous les contrôles souhaités ou ce que je veux.
Il y a deux choses que je fais. Dessiner des formes personnalisées et charger des bitmaps à partir de fichiers .png.
La ligne suivante verrouille le fichier lors du chargement (ce qui est un scénario indésirable):
Bitmap bmp = new Bitmap("file.png");
J'utilise donc une autre façon de charger le bitmap.
public static Bitmap LoadBitmap(string path) {
using (Bitmap original = new Bitmap(path))
{
return new Bitmap(original);
}
}
Je suppose que c'est beaucoup plus lent, mais je ne connais pas de meilleure façon de charger une image, tout en libérant rapidement le verrou de fichier.
Maintenant, lors du dessin d'une image, il existe une méthode que j'utilise:
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;
}
Cela semble cependant être une sorte de fuite de mémoire. Et si je continue à le faire trop longtemps, l'application se bloque finalement.
Par conséquent, j'ai 3 questions:
1.) Quelle est la meilleure façon de charger des bitmaps à partir de fichiers sans verrouiller le fichier?
2.) Quels objets doivent être éliminés manuellement dans la fonction Draw () (et dans quel ordre) pour qu'il n'y ait pas de fuite de mémoire et pas de lancement d'ObjectDisposedException?
3.) Si pictureBox.Image est défini sur bmp, comme dans la dernière ligne du code, pictureBox.Image.Dispose () ne disposerait que des ressources liés à la maintenance de la PictureBox.Image ou du bitmap sous-jacent qui lui est associé?
1: Je ne sais pas si cela fonctionne dans Windows Mobile, mais essayez ceci:
FileStream bitmapFile = new FileStream("mybitmap.bmp", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Image loaded = new Bitmap(bitmapFile);
2: le SolidBrush
doit être supprimé. Il existe une règle générale de disposition. -> "chaque objet, instancié par vous, qui implémente dispose doit être supprimé manuellement, sauf lorsque l'objet est une valeur de retour/ref/out"
Dans ce cas, il est préférable d'utiliser une instruction using
using (new objecttodispose){ ..... }
L'instruction using
assurera dans tous les cas l'appel de Dispose()
(exception par exemple).
3: Dispose()
libérera les ressources bitmap.
Je ne pense pas qu'il y ait une véritable fuite de mémoire . Le problème est que vous ne disposez pas de l'ancien bitmap, c'est au GC de nettoyer le truc. Mais il n'y a aucun moyen déterministe de dire quand cela se produira.
Donc, je pense que si vous allez parcourir beaucoup d'images, vous verrez une certaine augmentation de la mémoire et à un autre moment, elle tombera ou résistera à une position.
Je ne l'ai pas testé, mais peut-être que cela aidera un peu à le rendre plus déterministe:
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();
}
}
Et une autre idée inspirée de 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);
}
}
L'idée derrière mon deuxième exemple de code est de se débarrasser du descripteur de fichier et de copier le contenu du fichier dans la mémoire. Ensuite, le Bitmap deviendra propriétaire du MemoryStream qui sera supprimé dans mon premier échantillon en appelant la oldImage.Dispose()
.
En utilisant cette approche, il ne devrait jamais y avoir plus de deux images en mémoire, des milliers ne conduisant qu'à des OutOfMemoryExceptions par de très grandes images ou une petite quantité de RAM.