Domanda

Ho un TreeView che è legato a un albero di ViewModel istanze.Il problema è che il modello di dati provenienti da una lenta repository quindi ho bisogno di virtualizzazione dei dati.L'elenco dei sub ViewModel al di sotto di un nodo dovrebbe essere caricati solo quando il genitore vista ad albero nodo è ampliato e deve essere scaricato quando è crollato.

Come si può fare, pur nel rispetto MVVM principi?Come può il ViewModel ricevere una notifica che deve caricare o scaricare i sottonodi?Che è quando un nodo è stato espanso o compresso senza knowning nulla di treeview esistenza?

Qualcosa mi fa sentire che la virtualizzazione dei dati non va bene con MVVM.Poiché nella virtualizzazione dei dati ViewModel, generalmente, ha bisogno di sapere molto circa lo stato attuale dell'interfaccia utente e anche bisogno di controllare un sacco di aspetti dell'interfaccia utente.Prendiamo un altro esempio:

Una listview con la virtualizzazione dei dati.Il ViewModel avrebbe bisogno di controllare la lunghezza della ListView, scrollthumb in quanto dipende dal numero di elementi presenti nel Modello.Anche quando l'utente scorre, il ViewModel avrebbe bisogno di conoscere per quale posizione ha fatto scorrere e quanto è grande la listview è (come molti elementi attualmente adattarsi) per essere in grado di caricare la parte destra del Modello di modulo dati del repository.

È stato utile?

Soluzione

Il modo più semplice per risolvere questo problema, sia con una "virtualizzazione" raccolta di attuazione che mantiene debole riferimenti a suoi elementi lungo con un algoritmo per il recupero / creazione di oggetti.Il codice per questa collezione è piuttosto complesso, quello che con tutte le interfacce necessarie e le strutture di dati in modo efficiente traccia intervalli di dati caricati, ma qui è un parziale API per una classe che virtualizzate basate su indici:

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

La struttura interna dei dati qui è un equilibrato albero di intervalli di dati, con ogni intervallo di dati che contiene un indice iniziale e un array di riferimenti deboli.

Questo corso è progettato per essere una sottoclasse di fornire la logica per caricare i dati.Qui è come funziona:

  • Nella sottoclasse costruttore, RecordInsertOrDelete viene chiamato per impostare la dimensione dell'insieme iniziale
  • Quando un elemento è possibile accedere tramite IList/ICollection/IEnumerable, l'albero è utilizzato per trovare l'elemento di dati.Se nell'albero e c'è un debole di riferimento e i deboli ancora punti di riferimento per una vita oggetto, l'oggetto viene restituito, in caso contrario viene caricato e restituiti.
  • Quando una voce ha bisogno di essere caricato, un intervallo di indice è calcolato da una ricerca in avanti e indietro anche se la struttura di dati per la successiva/precedente già caricati, quindi astratto FetchItems si chiama così la sottoclasse può caricare gli elementi.
  • Nella sottoclasse FetchItems attuazione, gli elementi vengono recuperati e quindi RecordFetchedItems è chiamato ad aggiornare l'albero di intervalli con i nuovi elementi.Alcune complessità, qui è necessaria per unire nodi adiacenti per evitare l'eccessiva crescita di un albero.
  • Quando la sottoclasse riceve una notifica di dati esterni modifiche può chiamare RecordInsertOrDelete per aggiornare l'indice di tracking.Questa aggiornamenti inizio indici.Per un inserto, questo può anche dividere un intervallo, e per eliminare questo potrebbe richiedere uno o più intervalli di essere ricreato più piccoli.Questo stesso algoritmo viene utilizzato internamente quando gli elementi vengono aggiunti / eliminati attraverso l' IList e IList<T> interfacce.
  • Il Cleanup il metodo è chiamato in background in modo incrementale ricerca di albero di intervalli per l' WeakReferences e tutto intervalli che possono essere smaltiti, e anche per intervalli troppo scarsa (ad esempio: solo uno WeakReference in una gamma di 1000 slot)

Nota che FetchItems è passato un intervallo di scarico elementi in modo che si può utilizzare un metodo euristico per caricare di più elementi contemporaneamente.Un semplice tale euristica sarebbe caricamento successivo di 100 oggetti o fino al termine dell'attuale gap, a seconda di quale viene prima.

Con un VirtualizingCollection, WPF virtualizzazione integrata causa del caricamento dei dati al momento opportuno per ListBox, ComboBox, ecc, fintanto che si sta utilizzando, ad esempio. VirtualizingStackPanel invece di StackPanel.

Per un TreeView, un altro passo è necessario:Nel HierarchicalDataTemplate impostare un MultiBinding per ItemsSource che si lega al tuo reale ItemsSource e anche per IsExpanded sulla basati su modelli genitore.Il convertitore per il MultiBinding restituisce il primo valore (il ItemsSource) se il secondo valore (il IsExpanded valore) è true, altrimenti restituisce null.Che cosa questo non fa altro che fa in modo che quando si comprime un nodo in TreeView tutti i riferimenti per i contenuti della raccolta sono subito sceso in modo che VirtualizingCollection possibile pulire up.

Nota che la virtualizzazione non ha bisogno di essere fatto sulla base di indici.In una struttura ad albero scenario, può essere tutto o niente, e in un elenco scenario si stima che il numero può essere utilizzato e intervalli compilato come necessario, utilizzando un "tasto start" / "tasto di fine chiamata" il meccanismo.Questo è utile quando i dati sottostanti possono cambiare e virtualizzato vista traccia la sua attuale posizione in base a quale tasto è in cima alla schermata.

Altri suggerimenti

    <TreeView
        VirtualizingStackPanel.IsVirtualizing = "True"
        VirtualizingStackPanel.VirtualizationMode = "Recycling" 
VirtualizingStackPanel.CleanUpVirtualizedItem="TreeView_CleanUpVirtualizedItem">
            <TreeView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel />
                </ItemsPanelTemplate>
            </TreeView.ItemsPanel>
        </TreeView>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top