Загрузчик изображений с автоматической очисткой памяти

У меня есть список (простой 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/

Ответы на вопрос(1)

Ваш ответ на вопрос