Frage

Ich habe eine TreeView, die an einen Baum von Viewmodel Instanzen gebunden ist. Das Problem ist, dass die Modelldaten aus einem langsamen Repository kommen, damit ich Daten Virtualisierung benötigen. Die Liste der Unteransichtsmodell unterhalb eines Knotens nur geladen werden, wenn der übergeordnete Strukturansicht Knoten erweitert ist, und es sollte entladen werden, wenn es zusammengeklappt ist.

Wie kann dies realisiert werden, während auf MVVM Prinzipien haften? Wie kann das Ansichtsmodell benachrichtigt, dass es zum Laden oder Entladen untergeordneten Knoten benötigt? Das heißt, wenn ein Knoten erweitert oder reduziert, ohne etwas über die treeview Existenz knowning?

Etwas gibt mir das Gefühl, dass die Daten-Virtualisierung nicht gut mit MVVM geht. Da in Datenvirtualisierung das Ansichtsmodell im Allgemeinen viele Aspekte in der Benutzeroberfläche ganz steuern ziemlich viel über den aktuellen Stand der Benutzeroberfläche und aslo Bedürfnisse kennen muss. Nehmen wir ein anderes Beispiel:

Ein Listview mit Daten-Virtualisierung. Das Ansichtsmodell müßte die Länge des ScrollThumb des Listview steuern, da es auf der Anzahl der Elemente in dem Modell abhängt. Auch wenn der Benutzer blättert, müßte das Viewmodel bekannt, welche Position er gescrollt und wie groß die Listenansicht ist (wie viele Artikel zur Zeit in passen) in der Lage sein, den rechten Teil der Modelldaten zu laden, bilden das Repository.

War es hilfreich?

Lösung

Die einfache Möglichkeit, dies zu lösen, ist mit einer „virtualisieren Sammlung“ Implementierung, die zum Abrufen / Erstellen Elemente zusammen mit einem Algorithmus schwachen Verweise auf seine Elemente beibehält. Der Code für diese Sammlung ist ziemlich komplex, was mit allen erforderlichen Schnittstellen und die Datenstrukturen effizient Bereiche der geladenen Daten zu verfolgen, aber hier ist eine teilweise API für eine Klasse, die auf Indizes virtualisierten basiert:

public class VirtualizingCollection<T>
  : IList<T>, ICollection<T>, IEnumerable<T>,
    IList, ICollection, IEnumerable,
    INotifyPropertyChanged, INotifyCollectionChanged
{
  protected abstract void FetchItems(int requestedIndex, int gapStartIndex, int gapEndIndex);
  protected void RecordFetchedItems(int startIndex, int count, IEnumerable items) ...
  protected void RecordInsertOrDelete(int startIndex, int countPlusOrMinus) ...
  protected virtual void OnCollectionChanged(CollectionChangedEventArgs e) ...
  protected virtual void Cleanup();
}

Die interne Datenstruktur hier ist ein ausgeglichener Baum der Datenbereiche, wobei jeder Datenbereich einen Startindex und eine Reihe von schwachen Referenzen enthält.

Diese Klasse ist so konzipiert, subclassed wird, um die Logik zu schaffen, für die Daten tatsächlich geladen werden. Hier ist, wie es funktioniert:

  • In der Unterklasse Konstruktor RecordInsertOrDelete aufgerufen, um die erste Sammlung Größe festlegen
  • Wenn ein Element zugegriffen wird IList/ICollection/IEnumerable wird der Baum verwendet, um das Datenelement zu finden. Wenn in dem Baum gefunden und es ist ein schwacher Verweis und die schwache Referenz noch Punkte auf ein Leben Objekt, das Objekt zurückgegeben wird, sonst wird es geladen und zurückgegeben.
  • Wenn ein Element Bedarf geladen wird, wird ein Indexbereich berechnet, indem vor und zurück Suche obwohl die Datenstruktur für das nächste / vorherige bereits geladene Element, dann die abstrakte FetchItems genannt wird, so dass die Unterklasse der Elemente laden kann.
  • In der Unterklasse FetchItems Implementierung Elemente abgerufen werden, und dann wird RecordFetchedItems genannt, den Baum der Bereiche mit den neuen Sachen zu aktualisieren. Einige Komplexität hier erforderlich ist, um benachbarte Knoten zu verschmelzen zu viel Baumwachstum zu verhindern.
  • Wenn die Unterklasse eine Benachrichtigung externer Daten wird verändert es RecordInsertOrDelete nennen kann den Index-Tracking zu aktualisieren. Diese Updates starten Indizes. Für einen Einsatz, teilen kann dies auch eine Reihe, und für eine dieser kleineren löschen können einen oder mehrere Bereiche erfordern neu erstellt. Derselbe Algorithmus wird intern verwendet, wenn Elemente hinzugefügt / gelöscht durch das IList und IList<T> Schnittstellen.
  • Die Cleanup Methode wird im Hintergrund genannt, den Baum von Bereichen für WeakReferences und ganze Bereiche inkrementell zu suchen, die entsorgt werden können, und auch für Bereiche, die zu dünn sind (zB nur ein WeakReference in einem Bereich mit 1000 Slots)

Beachten Sie, dass FetchItems eine Reihe von unbelasteten Elemente geleitet wird, so dass es eine Heuristik verwenden können mehrere Elemente auf einmal zu laden. Eine einfache solche Heuristik würde die nächsten 100 Elemente oder bis zum Ende der aktuellen Lücke geladen, je nachdem, was zuerst eintritt.

Mit einem VirtualizingCollection, WPF-interner Virtualisierung Laden von Daten zu den entsprechenden Zeiten für ListBox verursachen, ComboBox, etc., solange Sie zB verwenden. VirtualizingStackPanel statt StackPanel.

Für eine TreeView, einen weiteren Schritt erforderlich: In der HierarchicalDataTemplate eine MultiBinding für ItemsSource eingestellt, dass bindet an Ihren wirklichen ItemsSource und auch IsExpanded auf dem Templat Elternteil. Der Konverter für das MultiBinding gibt seinen ersten Wert (der ItemsSource), wenn der zweite Wert (der Wert IsExpanded) wahr ist, ansonsten kehrt sie null. Was dies bedeutet ist, macht es so, dass, wenn Sie alle Verweise auf die Sammlung Inhalt eines Knotens in der TreeView kollabieren werden sofort gelöscht, so dass VirtualizingCollection ihnen reinigen.

Beachten Sie, dass die Virtualisierung muss nicht basierend auf Indizes erfolgen. In einem Baum Szenario kann es alles oder nichts, und in einer Liste Szenario zu einer geschätzten Zahl verwendet und Bereich in nach Bedarf mit einem „Start-Taste“ / „Ende-Taste“ Mechanismus gefüllt werden kann. Dies ist nützlich, wenn die zugrunde liegenden Daten ändern können und die virtualisierte Ansicht sollte seinen aktuellen Standort basierend auf welcher Taste ist am oberen Rande des Bildschirms.

Andere Tipps

    <TreeView
        VirtualizingStackPanel.IsVirtualizing = "True"
        VirtualizingStackPanel.VirtualizationMode = "Recycling" 
VirtualizingStackPanel.CleanUpVirtualizedItem="TreeView_CleanUpVirtualizedItem">
            <TreeView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel />
                </ItemsPanelTemplate>
            </TreeView.ItemsPanel>
        </TreeView>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top