Bild-Downloader mit automatischer Speicherbereinigung

Ich habe eine Liste (einfache ListBox) von Elementen mit Bildern auf Master-Detail-Basis (wenn der Benutzer auf ein Listenelement klickt, wird die Detailseite geöffnet). Ich hatte ein ziemlich bekanntes Problem mit Speicherlecks bei Bildern, beschriebenHier, Hier, Hier, undHier.

Ein möglicher Weg ist zuAlle Bilder durchlaufen beim NavigierenVonund säubere sie.

Imeiner der ThreadsIch fand eine interessantere Lösung: Es bereinigt Bilder automatisch, aber das funktioniert nicht für die Virtualisierung (Bilder gehen verloren oder werden gemischt, wenn ein privates Feld zum Speichern von ImageSource hinzugefügt wird). Es wurde empfohlen, eine Abhängigkeitseigenschaft hinzuzufügen.

Aber ich stehe immer noch vor dem gleichen Problem: Bilder werden nach dem Scrollen nach unten und nach oben vertauscht. Das Aussehen von Abhängigkeitseigenschaften wird zufällig geändert, aber ich kann den Moment nicht erfassen, in dem sie sich ändern.

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

Verwendungszweck:

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

Auf den ersten Blick funktioniert es also, aber wenn Sie nach unten scrollen und wieder nach oben gehen, werden die Bilder nach dem Zufallsprinzip geändert.

BEARBEITEN: in diesem Fall verwende ich im Moment nur reine ListBox, ohne Virtualisierung (aber in anderen Fällen zu erwarten).

EDIT2: Beispielprojekt, um dieses Problem zu reproduzieren. Ich glaube, es würde in einer Weile eine Lösung enthalten:https://simca.codeplex.com/

Antworten auf die Frage(1)

Ihre Antwort auf die Frage