Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dispose BitmapImage cache?

Tags:

c#

wpf

I'm facing a memory leak issue. The leak comes from here:

public static BitmapSource BitmapImageFromFile(string filepath)
{
    BitmapImage bi = new BitmapImage();

    bi.BeginInit();
    bi.CacheOption = BitmapCacheOption.OnLoad; //here
    bi.CreateOptions = BitmapCreateOptions.IgnoreImageCache; //and here
    bi.UriSource = new Uri(filepath, UriKind.RelativeOrAbsolute);
    bi.EndInit();

    return bi;
}

I have a ScatterViewItem, which contains an Image, and the source is the BitmapImage of this function.

The actual thing is a lot more complex than this, so I can't simply put an Image into it. I also can't use the default load options, as the image file might be deleted and hence will face some permission issue accessing the file during deletion.

The problem occurs when I close the ScatterViewItem, which in turn closes the Image. However, the cached memory isnt cleared. So after many cycles, the memory consumption is pretty big.

I tried setting image.Source=null during the Unloaded function, but it didn't clear it.

How do I clear the memory correctly during unloading?

like image 379
Darren Ng Avatar asked Feb 06 '15 11:02

Darren Ng


1 Answers

I found the answer here. Seems like it's a bug in WPF.

I modified the function to include Freeze:

public static BitmapSource BitmapImageFromFile(string filepath)
{
    var bi = new BitmapImage();

    using (var fs = new FileStream(filepath, FileMode.Open))
    {
        bi.BeginInit();                
        bi.StreamSource = fs;                
        bi.CacheOption = BitmapCacheOption.OnLoad;
        bi.EndInit();
    }

    bi.Freeze(); //Important to freeze it, otherwise it will still have minor leaks

    return bi;
}

I also create my own Close function, which will be called before I close the ScatterViewItem:

public void Close()
{
    myImage.Source = null;
    UpdateLayout();
    GC.Collect();
}  

Because myImage is hosted in a ScatterViewItem, GC.Collect() must be called before the parent is closed. Otherwise, it will still linger in memory.

like image 108
Darren Ng Avatar answered Oct 14 '22 19:10

Darren Ng