Why do I get an OutOfMemoryException when I have images in my ListBox?
Solution 1:
Oh, I recently killed whole day to make this working!
So the solution is:
Make your Image control free resources. So set the
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
as it was mentioned before.
Make sure you virtualize _bitmap on every item of the list. You should load it on demand (LongListSelector.Realized method) and you have to destroy it! It won't going to collect automatically and GC.Collect doesn't work either. Null reference is not working too :( But here is the method: Make 1x1 pixel file. Copy it into assembly and make resource stream from it to dispose your images with 1x1 pixel blank. Bind custom dispose method to LongListSelector.UnRealized event (e.Container handles your list item).
public static void DisposeImage(BitmapImage image)
{
Uri uri= new Uri("oneXone.png", UriKind.Relative);
StreamResourceInfo sr=Application.GetResourceStream(uri);
try
{
using (Stream stream=sr.Stream)
{
image.DecodePixelWidth=1; //This is essential!
image.SetSource(stream);
}
}
catch { }
}
Working for me in LongListSelector with 1000 images 400 width each.
If you miss the 2 step with the data collection you can see the the good results but the memory overflows after 100-200 items scrolled.
Solution 2:
You just had Windows Phone with show all the picture's in a user's media library "pictures" folder on screen. That's unbelievably memory intensive and considering the 150MB limit on WP8 apps it's no wonder you're getting OOM exceptions.
A few things you should consider adding:
1) Set Source and SourceUri properties to null when scrolling the listboxitem out of view. See "Caching Images" in Stefan's article here @ http://blogs.msdn.com/b/swick/archive/2011/04/07/image-tips-for-windows-phone-7.aspx
BitmapImage bitmapImage = image.Source as BitmapImage;
bitmapImage.UriSource = null;
image.Source = null;
2) If you're on WP8 make sure to set DecodePixelWidth and/or DecodePixelHeight. That way an image will be loaded into memory, resized permanently and only the resized copy is stored in memory. The images loaded into memory can be much bigger then the screen size of the phone itself. So cropping those down to the right size and only storing the resized images is vital. Set BitmapImage.DecodePixelWidth=480 (at maximum) to help with that.
var bmp = new BitmapImage();
// no matter the actual size,
// this bitmap is decoded to 480 pixels width (aspect ratio preserved)
// and only takes up the memory needed for this size
bmp.DecodePixelWidth = 480;
bmp.UriSource = new Uri(@"Assets\Demo.png", UriKind.Relative);
ImageControl.Source = bmp;
(code sample from here)
3) Why are you using Picture.GetImage() instead of Picture.GetThumbnail()? Do you really need the image to take up the whole screen?
4) Consider moving from ListBox to LongListSelector if this is a WP8 exclusive app. LLS has much, much better virtualization then ListBox. Looking at your code sample it might be enough for you to just change your XAML ListBox element to LongListSelector element.