Загрузчик изображений с автоматической очисткой памяти
У меня есть список (простой ListBox) элементов с изображениями на основе master-detail (если пользователь нажимает на элемент списка, открывается страница сведений). Я столкнулся с довольно известной проблемой с утечками памяти изображений, описаннойВот, Вот, Вот, а такжеВот.
Одним из возможных способов являетсяпробежать все изображения когда NavigatingFromи почистить их.
Водин из потоковЯ нашел более интересное решение: он очищает изображения автоматически, но это не работает для виртуализации (изображения теряются или смешиваются, если добавить личное поле для хранения ImageSource). Предложенное исправление состояло в том, чтобы добавить свойство зависимости.
Но я все еще сталкиваюсь с той же проблемой: изображения смешиваются после прокрутки вниз и возврата вверх. Похоже, свойства зависимостей меняются случайным образом, но я не могу уловить момент, когда они меняются.
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;
}
}
Использование:
<wpExtensions:SafePicture SafePath="{Binding ImageUrl}"/>
Итак, на первый взгляд все работает нормально, но если прокрутить вниз и вернуться вверх, изображения меняются случайным образом.
РЕДАКТИРОВАТЬ: в этом случае, на данный момент, я использую только чистый ListBox, без виртуализации (но ожидаю этого в других случаях).
EDIT2: пример проекта для воспроизведения этой проблемы. Я полагаю, что это будет содержать решение через некоторое время:https://simca.codeplex.com/