I have a list (simple ListBox) of items with images on master-detail base (if user clicks on list item, details page is opened). I faced quite famous problem with images memory leaks, described here, here, here, and here.
One possible way is to run through all images when NavigatingFrom and clean them.
In one of the threads, i found more interesting solution: it cleans images automatically, but that is not working for virtualization (images are lost or mixed, if to add private field for storing ImageSource). Suggested fix was to add dependency property.
But i'm still facing the same problem: images are mixed up after scrolling down and returning up. Looking like dependency property are changed randomly, but i cant catch the moment when they are changing.
public class SafePicture : ContentControl
{
public static readonly DependencyProperty SafePathProperty =
DependencyProperty.RegisterAttached(
"SafePath",
typeof(string),
typeof(SafePicture),
new PropertyMetadata(OnSourceWithCustomRefererChanged));
public string SafePath
{
get { return (string)GetValue(SafePathProperty); }
set { SetValue(SafePathProperty, value); }
}
private static void OnSourceWithCustomRefererChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null) // New value here
return;
}
public SafePicture()
{
Content = new Image();
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
private void OnLoaded(object _sender, RoutedEventArgs _routedEventArgs)
{
var image = Content as Image;
if (image == null)
return;
var path = (string)GetValue(SafePathProperty); // Also, tried SafePath (debugger cant catch setter and getter calls), but same result.
image.Source = null;
{
var request = WebRequest.Create(path) as HttpWebRequest;
request.AllowReadStreamBuffering = true;
request.BeginGetResponse(result =>
{
try
{
Stream imageStream = request.EndGetResponse(result).GetResponseStream();
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
if (imageStream == null)
{
image.Source = new BitmapImage { UriSource = new Uri(path, UriKind.Relative) };
return;
}
var bitmapImage = new BitmapImage();
bitmapImage.CreateOptions = BitmapCreateOptions.BackgroundCreation;
bitmapImage.SetSource(imageStream);
image.Source = bitmapImage;
});
}
catch (WebException)
{
}
}, null);
}
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
var image = Content as Image;
if (image == null)
return;
var bitmapImage = image.Source as BitmapImage;
if (bitmapImage != null)
bitmapImage.UriSource = null;
image.Source = null;
}
}
Usage:
<wpExtensions:SafePicture SafePath="{Binding ImageUrl}"/>
So, at first glance, it works fine, but if to scroll down and return up, images are changed randomly.
EDIT: in this case, for now, i'm using only pure ListBox, without virtualization (but expecting it in other cases).
EDIT2: sample project to reproduce this problem. I believe, it would contain solution in a while: https://simca.codeplex.com/