4

从 ImageList 获取图像时,我遇到了 OutOfMemoryException 我一直无法找到问题的适当解决方案。

我有一个自定义 ListView 控件,它附加了一个用于绘制 ListViewItems 的事件。然后这会调用一个静态方法,该方法旨在绘制项目。

对于大约 300 个项目的 ListView,每次滚动 ListView 时,我们都会使内存增加大约 100Mb。违规代码已被追踪到以下内容:

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 缓慢地重新绘制自身,并且在它尝试重新绘制时最终会在屏幕上出现伪影。

有没有其他人经历过这个?或者知道解决方法?

4

2 回答 2

4

你在处置graphics吗?此外,如果您像您提到的那样处理您的图像,那么您需要确保将其从 ImageList 中取出,否则您将导致更多问题。图片是什么格式的?

通常,当您在涉及图像时出现内存不足问题时,您的问题将是某些方法不喜欢某些图像格式,或者 9/10 次,您误解了其中一个图形对象的生命周期。

  • 检查所有Graphics使用情况并将它们放在using块中。
  • 检查你的Image生命周期并小心复制它们、处理它们、关闭底层流等。
  • 加载一个内存管理器(VS2008 内置了一个),看看有什么没有得到很好的清理。

编辑:

这是我能找到的最佳选择,使用ImageList.Draw(graphics, x, y, width, height, index). 这将使用内部句柄而不是创建图像的副本。

于 2009-05-20T04:42:56.187 回答
0

我已经设法在我的应用程序中解决了这个问题。

杰森有答案,你必须确保你使用“使用”块,或者它们的等价物。

我使用 VB,等效于使用 Try...Catch... 最后,每当我创建一个新位图时,调用 BitMap.Dispose 并在“Finally”部分设置 Bitmap = nothing。

这似乎是一个非常普遍的问题,从我在谷歌上花费的时间来看。下面的代码还允许任何图像在缩小为缩略图时保持其纵横比,这是 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
于 2009-06-11T19:08:40.187 回答