ImageList / OutOfMemoryException Imagen
-
22-08-2019 - |
Pregunta
Estoy sufriendo de un OutOfMemoryException al obtener una imagen de un ImageList he sido incapaz de encontrar una solución adecuada al problema.
Tengo un control ListView personalizado, que se ha unido a él un evento para el dibujo de ListViewItems. Esto requiere entonces un método estático que está diseñado para sacar el artículo.
En un ListView de alrededor de 300 artículos, que estamos recibiendo la memoria saltar alrededor de 100 Mb cada vez que se desplaza el ListView. El código erróneo ha sido rastreado a lo siguiente:
Image image = item.ImageList.Images[item.ImageKey];
if (image != null)
{
Size imageOffset = new Size((bounds.Width - image.Width) / 2, 2);
Point imagePosition = bounds.Location + imageOffset;
graphics.DrawImageUnscaled(image, imagePosition);
}
Parece (ciertamente en WinXP) que la recolección de basura no está funcionando correctamente, causando la memoria en espiral. Hemos intentado añadir un image.Dispose () directamente después del bloque de código para solucionar el problema, pero que no tiene ningún efecto.
La única solución que he logrado encontrar hasta el momento, se encuentra al final del método estático para llamar GC.Collect (). El problema con esto sin embargo es que hace entonces que el ListView para volver a pintarse lentamente y se llega a artefactos en la pantalla mientras intenta volver a dibujar.
¿Alguien más ha experimentado esto? O sabe de alguna solución?
Solución
¿Está desechando graphics
? Además, se deshaga su imagen como usted ha mencionado a continuación, lo que se necesita para asegurarse de que se saca de la ImageList o va a causar más problemas. Lo qué formato son las imágenes?
En general, cuando usted sale de problemas de memoria cuando se trata de imágenes, su problema será o bien algún método no le gusta un poco de formato de imagen, o 9/10 veces, se entendió mal el ciclo de vida de uno de los objetos gráficos.
- Marque todo su uso de
Graphics
y ponerlos en bloquesusing
. - Compruebe su ciclo de vida
Image
y tener cuidado con la copia de ellos, disponiéndolos, cerrando las corrientes subyacentes, etc. - Carga un administrador de memoria (VS2008 tiene uno interno) y ver lo que no está siendo limpiado muy bien.
EDIT:
Esta es la mejor opción que puedo encontrar, usar ImageList.Draw(graphics, x, y, width, height, index)
. Esto utilizará la manija interna en lugar de crear una copia de la imagen.
Otros consejos
He logrado resolver este problema en mi solicitud.
Jason tiene la respuesta, usted tiene que asegurarse de que utiliza "utilizando" bloques, o su equivalente.
Yo uso VB, y el equivalente era utilizar Try ... Catch ... Finally siempre he creado un nuevo mapa de bits, llamando Bitmap.Dispose y establecer el mapa de bits = nada en la parte de "último".
Esto parece ser un problema muy común, a juzgar por las horas que he pasado tratando de Google esto. El código de abajo también permite que cualquier imagen para conservar su relación de aspecto cuando se reduzca a una miniatura, otra cuestión que parece ser difícil de Google!
Código:
Private Function AspectedImage(ByVal ImagePath As String, ByVal SizeWanted As Integer) As Image
Dim myBitmap, WhiteSpace As System.Drawing.Bitmap
Dim myGraphics As Graphics
Dim myDestination As Rectangle
Dim MaxDimension As Integer
Dim ReductionRatio As Double
Try
'create an instance of bitmap based on a file
myBitmap = New System.Drawing.Bitmap(ImagePath)
'create a new square blank bitmap the right size
If myBitmap.Height >= myBitmap.Width Then MaxDimension = myBitmap.Height Else MaxDimension = myBitmap.Width
ReductionRatio = SizeWanted / MaxDimension
WhiteSpace = New System.Drawing.Bitmap(SizeWanted, SizeWanted)
'get the drawing surface of the new blank bitmap
myGraphics = Graphics.FromImage(WhiteSpace)
'find out if the photo is landscape or portrait
Dim WhiteGap As Double
If myBitmap.Height > myBitmap.Width Then 'portrait
WhiteGap = ((myBitmap.Width - myBitmap.Height) / 2) * -1
myDestination = New Rectangle(x:=CInt(WhiteGap * ReductionRatio), y:=0, Width:=Int(myBitmap.Width * ReductionRatio), Height:=Int(myBitmap.Height * ReductionRatio))
Else 'landscape
WhiteGap = ((myBitmap.Width - myBitmap.Height) / 2)
'create a destination rectangle
myDestination = New Rectangle(x:=0, y:=CInt(WhiteGap * ReductionRatio), Width:=Int(myBitmap.Width * ReductionRatio), Height:=Int(myBitmap.Height * ReductionRatio))
End If
'draw the image on the white square
myGraphics.DrawImage(image:=myBitmap, rect:=myDestination)
AspectedImage = WhiteSpace
Catch ex As Exception
myBitmap = New System.Drawing.Bitmap("")
AspectedImage = New System.Drawing.Bitmap(4, 4)
ImageBufferExceeded = True
MsgBox("Image Buffer exceeded, too many images in memory. If the one(s) you want can't be seen, please restart the application and navigate straight to your images")
Finally
myBitmap.Dispose()
myBitmap = Nothing
WhiteSpace = Nothing
End Try
End Function