ImageList / Image OutOfMemoryException - PullRequest
4 голосов
/ 19 мая 2009

Я страдаю от исключения OutOfMemoryException при получении изображения из ImageList. Мне не удалось найти подходящее решение проблемы.

У меня есть пользовательский элемент управления ListView, который прикрепил к нему событие для рисования ListViewItems. Затем вызывается статический метод, предназначенный для рисования элемента.

Для ListView из примерно 300 элементов мы получаем скачок памяти примерно на 100 Мб каждый раз, когда ListView прокручивается. Код ошибки был отслежен следующим образом:

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);
}

Кажется (конечно, в WinXP), что сборка мусора работает неправильно, вызывая спираль памяти. Мы попытались добавить image.Dispose () сразу после блока кода, чтобы исправить проблему, но это не имеет никакого эффекта.

Единственное решение, которое мне удалось найти, это конец статического метода для вызова GC.Collect (). Однако проблема заключается в том, что после этого ListView медленно перерисовывает себя, и в результате вы получаете артефакты на экране при попытке перерисовки.

Кто-нибудь еще испытывал это? Или знает об обходном пути?

Ответы [ 2 ]

4 голосов
/ 20 мая 2009

Вы утилизируете graphics? Кроме того, если вы удалите свое изображение, как вы упомянули, вам нужно будет убедиться, что оно удалено из ImageList, иначе вы вызовете больше проблем. Какой формат изображения?

В общем, когда у вас возникают проблемы с памятью, когда задействованы изображения, ваша проблема будет в том, что какой-то метод не любит какой-либо формат изображения, или 9/10 раз вы неправильно поняли жизненный цикл одного из графических объектов.

  • Проверьте все ваши Graphics использования и поместите их в using блоков.
  • Проверьте ваш жизненный цикл Image и будьте осторожны при копировании, утилизации, закрытии основных потоков и т. Д.
  • Загрузите диспетчер памяти (VS2008 имеет встроенный) и посмотрите, что не очень хорошо очищается.

EDIT:

Вот лучший вариант, который я могу найти, используйте <a href="http://msdn.microsoft.com/en-us/library/aa335209(VS.71).aspx" rel="nofollow noreferrer">ImageList.Draw</a>(graphics, x, y, width, height, index). При этом вместо создания копии изображения будет использоваться внутренний дескриптор.

0 голосов
/ 11 июня 2009

Мне удалось решить эту проблему в моем приложении.

У Джейсона есть ответ, вы должны убедиться, что используете блоки "using" или их эквиваленты.

Я использую VB, и эквивалентно было использовать Try ... Catch ... Наконец, всякий раз, когда я создавал новое растровое изображение, вызывая BitMap.Dispose и устанавливая Bitmap = none в части "Наконец".

Кажется, это действительно распространенная проблема, если судить по тем часам, которые я потратил, пытаясь найти Google. Приведенный ниже код также позволяет любому изображению сохранять свое соотношение сторон при уменьшении до миниатюры, что является еще одной проблемой, которая кажется Google трудной!

Код:

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
...