Pregunta

Tengo un TreeView que está unido a un árbol de instancias ViewModel. El problema es que los datos del modelo proviene de un repositorio lenta, así que necesito la virtualización de datos. La lista de sub modelo de vista por debajo de un nodo sólo se debe cargar cuando se expande la vista de árbol nodo padre y debe ser descargada cuando se derrumbó.

¿Cómo puede ser implementada mientras que se adhiere a los principios MVVM? ¿Cómo puede el modelo de vista recibirá una notificación de que necesita subnodos carga o la descarga? Es decir, cuando un nodo se expandir o contraer sin knowning nada acerca de la existencia de la vista de árbol?

Algo me hace sentir que la virtualización de datos no va bien con MVVM. Dado que en la virtualización de datos del modelo de vista general, necesita saber mucho sobre el estado actual de la interfaz de usuario y aslo necesidades para el control de un buen montón de aspectos de la interfaz de usuario. Tomemos otro ejemplo:

A listview con la virtualización de datos. El modelo de vista tendría que controlar la longitud de scrollthumb del ListView ya que depende del número de elementos en el modelo. Además, cuando el usuario se desplaza, el modelo de vista tendría que conocer a qué posición se desplaza hacia y cuán grande es la vista de lista es (el número de elementos encajan actualmente en) para poder cargar la parte derecha de datos del modelo formar el repositorio.

¿Fue útil?

Solución

La forma más fácil de resolver esto es con una aplicación "colección de virtualización" que mantiene las referencias débiles a sus elementos, junto con un algoritmo para ir a buscar / crear artículos. El código para esta colección es bastante complejo, lo que con todas las interfaces necesarias y las estructuras de datos para realizar un seguimiento eficiente rangos de datos cargados, pero aquí hay una API parcial para una clase que virtualizada basada en índices:

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 estructura de datos interna aquí es un árbol de equilibrado de rangos de datos, con los datos de cada intervalo que contiene un índice de inicio y una serie de referencias débiles.

Esta clase está diseñada para ser subclase para proporcionar la lógica para realmente la carga de los datos. Aquí es cómo funciona:

  • En el constructor de la subclase, RecordInsertOrDelete se llama para establecer el tamaño inicial colección
  • Cuando se accede a un elemento utilizando IList/ICollection/IEnumerable, el árbol se utiliza para encontrar el elemento de datos. Si se encuentra en el árbol y hay una referencia débil y el débil referencia aún apunta a un objeto de la vida, se devuelve ese objeto, de lo contrario, se carga y se volvió.
  • Cuando un necesidades objeto sea cargado, un rango de índice se calcula mediante la búsqueda hacia adelante y hacia atrás, aunque la estructura de datos para el objeto ya-cargada siguiente / anterior, entonces el FetchItems abstracto se llama por lo que la subclase puede cargar los artículos.
  • En la implementación subclase FetchItems, artículos son descabellada y luego RecordFetchedItems se llama para actualizar el árbol de rangos con los nuevos elementos. Cierta complejidad que aquí se requiere para fusionar los nodos adyacentes para evitar un crecimiento excesivo del árbol.
  • Cuando la subclase recibe una notificación de los datos externos cambia puede llamar RecordInsertOrDelete para actualizar el índice de seguimiento. Este actualizaciones comienzan índices. Para una inserción, esto también puede dividir un rango, y por un Suprimir esta puede requerir uno o más rangos de volver a crear más pequeño. Este mismo algoritmo se utiliza internamente cuando se añaden elementos / eliminado a través de las interfaces de IList y IList<T>.
  • El método Cleanup se llama en el fondo para buscar incrementalmente el árbol de rangos para WeakReferences y rangos enteros que pueden ser eliminados, y también para gamas que son demasiado escaso (por ejemplo, sólo una WeakReference en un intervalo con 1000 slots)

Tenga en cuenta que FetchItems se pasa a una gama de artículos descargados por lo que puede utilizar una heurística para cargar varios elementos a la vez. Un simple ejemplo heurística sería la carga de los próximos 100 artículos o hasta el final de la brecha actual, lo que ocurra primero.

Con una VirtualizingCollection, WPF de virtualización integrada hará que la carga de datos en el momento apropiado para ListBox, ComboBox, etc, siempre y cuando se está utilizando por ejemplo. VirtualizingStackPanel en lugar de StackPanel.

Para una TreeView, se requiere un paso más: En el HierarchicalDataTemplate estableció un MultiBinding para ItemsSource que se une a su ItemsSource real y también a IsExpanded en la matriz de plantilla. El convertidor para la MultiBinding devuelve su primer valor (el ItemsSource) si el segundo valor (el valor IsExpanded) es verdad, de lo contrario devuelve null. Lo que esto hace es lo hace de manera que cuando se contrae un nodo en el TreeView todas las referencias a los contenidos de la colección se dejan caer inmediatamente para que VirtualizingCollection puede limpiarlos.

Tenga en cuenta que la necesidad de virtualización no se realiza en base a los índices. En un escenario de árbol, que puede ser todo o nada, y en un escenario de una lista de recuento estimado puede ser utilizado y rangos de llenado de la manera necesaria el uso de un mecanismo de "tecla de inicio" / "tecla de finalización". Esto es útil cuando los datos subyacentes pueden cambiar y la vista virtualizado deben realizar un seguimiento de su ubicación actual en base a qué tecla que está en la parte superior de la pantalla.

Otros consejos

    <TreeView
        VirtualizingStackPanel.IsVirtualizing = "True"
        VirtualizingStackPanel.VirtualizationMode = "Recycling" 
VirtualizingStackPanel.CleanUpVirtualizedItem="TreeView_CleanUpVirtualizedItem">
            <TreeView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel />
                </ItemsPanelTemplate>
            </TreeView.ItemsPanel>
        </TreeView>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top