Program do pobierania obrazów z automatycznym czyszczeniem pamięci

Mam listę (prosty ListBox) elementów z obrazami na podstawie głównego detalu (jeśli użytkownik kliknie element listy, strona szczegółów zostanie otwarta). Opisałem dość znany problem przecieków pamięci obrazówtutaj, tutaj, tutaj, itutaj.

Jednym z możliwych sposobów jestprzeglądać wszystkie obrazy podczas nawigacji odi wyczyść je.

Wjeden z wątków, znalazłem bardziej interesujące rozwiązanie: czyści obrazy automatycznie, ale to nie działa dla wirtualizacji (obrazy są gubione lub mieszane, jeśli dodać prywatne pole do przechowywania ImageSource). Sugerowaną poprawką było dodanie właściwości zależności.

Ale wciąż mam ten sam problem: obrazy są pomieszane po przewinięciu w dół i powrocie. Wyglądanie jak własność zależności jest zmieniane losowo, ale nie mogę uchwycić momentu, kiedy się zmieniają.

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;
    }
}

Stosowanie:

<wpExtensions:SafePicture SafePath="{Binding ImageUrl}"/>

Na pierwszy rzut oka działa dobrze, ale jeśli przewiniesz w dół i powrócisz, obrazy zostaną zmienione losowo.

EDYCJA: w tym przypadku używam teraz tylko czystego ListBox, bez wirtualizacji (ale oczekuję tego w innych przypadkach).

EDIT2: przykładowy projekt do odtworzenia tego problemu. Wierzę, że za chwilę zawierałoby rozwiązanie:https://simca.codeplex.com/

questionAnswers(1)

yourAnswerToTheQuestion