4

ImageList から Image を取得するときに OutOfMemoryException が発生し、問題の適切な解決策を見つけることができませんでした。

ListViewItems を描画するためのイベントが関連付けられたカスタム ListView コントロールがあります。次に、アイテムを描画するように設計された静的メソッドを呼び出します。

約 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 回、グラフィック オブジェクトの 1 つのライフサイクルを誤解していることです。

  • すべてのGraphics使用状況を確認し、それらをusingブロックに入れます。
  • ライフ サイクルを確認Imageし、コピー、破棄、基になるストリームの終了などに注意してください。
  • メモリ マネージャー (VS2008 には 1 つが組み込まれています) をロードし、何がきれいにクリーンアップされていないかを確認します。

編集:

これが私が見つけることができる最良のオプションですImageList.Draw(graphics, x, y, width, height, index)。これは、イメージのコピーを作成する代わりに、内部ハンドルを使用します。

于 2009-05-20T04:42:56.187 に答える
0

私は自分のアプリケーションでこの問題を解決することができました。

Jason は答えを持っています。「using」ブロックまたは同等のものを使用していることを確認する必要があります。

私は 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 に答える