La solution la plus simple consiste à utiliser une implémentation de "virtualisation de collection" qui conserve des références faibles à ses éléments, ainsi qu'un algorithme de récupération/création d'éléments. Le code de cette collection est assez complexe, ce avec toutes les interfaces nécessaires et les structures de données pour suivre efficacement les plages de données chargées, mais voici une API partielle pour une classe qui virtualisée basée sur les indices:
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();
}
L'interne La structure de données est ici une arborescence équilibrée de plages de données, chaque plage de données contenant un index de départ et un tableau de références faibles.
Cette classe est conçue pour être sous-classée afin de fournir la logique de chargement effectif des données. Voilà comment cela fonctionne:
- Dans le constructeur de sous-classe,
RecordInsertOrDelete
est appelé à définir la taille de la collection initiale
- Lorsqu'un élément est accessible à l'aide
IList/ICollection/IEnumerable
, l'arbre est utilisé pour trouver l'élément de données. Si trouvé dans l'arbre et qu'il y a une référence faible et que la référence faible pointe toujours vers un objet de vie, cet objet est retourné, sinon il est chargé et retourné.
- Lorsqu'un élément doit être chargé, une plage d'index est calculée en recherchant en avant et en arrière la structure de données pour l'élément suivant/précédent déjà chargé, puis l'abrégé
FetchItems
est appelé afin que la sous-classe puisse charger les éléments.
- Dans l'implémentation de la sous-classe
FetchItems
, les éléments sont récupérés puis RecordFetchedItems
est appelée pour mettre à jour l'arborescence des plages avec les nouveaux éléments. Une certaine complexité est nécessaire pour fusionner les nœuds adjacents afin d'éviter une trop grande croissance des arbres.
- Lorsque la sous-classe reçoit une notification de modifications de données externes, elle peut appeler
RecordInsertOrDelete
pour mettre à jour le suivi d'index. Cela met à jour les index de démarrage. Pour une insertion, cela peut également diviser une plage, et pour une suppression, cela peut nécessiter de recréer une ou plusieurs plages. Ce même algorithme est utilisé en interne lorsque des éléments sont ajoutés/supprimés via les interfaces IList
et IList<T>
.
- La méthode
Cleanup
est appelée dans le contexte de la recherche de façon incrémentielle l'arbre de gammes pour WeakReferences
et les plages entières qui peuvent être disposés, ainsi que pour les gammes qui sont trop rares (par exemple seulement une WeakReference
dans une plage de 1000 emplacements)
Notez que FetchItems
est passé une gamme d'éléments déchargés afin qu'il puisse utiliser une heuristique pour charger plusieurs éléments à la fois. Une telle heuristique simple consisterait à charger les 100 éléments suivants ou jusqu'à la fin de l'intervalle actuel, selon la première éventualité.
Avec un VirtualizingCollection
, la virtualisation intégrée WPF provoquera le chargement de données au moment opportun pour ListBox
, ComboBox
, etc, aussi longtemps que vous utilisez par exemple. VirtualizingStackPanel
au lieu de StackPanel
.
Pour TreeView
, il en faut pas de plus: Dans le HierarchicalDataTemplate
établi un MultiBinding
pour ItemsSource
qui se lie à votre vrai ItemsSource
et aussi IsExpanded
sur le parent basé sur un modèle. Le convertisseur pour le MultiBinding
renvoie sa première valeur (ItemsSource
) si la deuxième valeur (la valeur IsExpanded
) est vraie, sinon elle renvoie null. Cela fait en sorte que lorsque vous réduisez un nœud dans le TreeView
, toutes les références au contenu de la collection sont immédiatement supprimées afin que VirtualizingCollection
puisse les nettoyer.
Notez que la virtualisation n'a pas besoin d'être effectuée en fonction des index. Dans un scénario en arbre, il peut s'agir de tout ou rien, et dans un scénario de liste, un compte estimé peut être utilisé et des plages remplies si nécessaire en utilisant un mécanisme de "clé de départ"/"touche de fin". Ceci est utile lorsque les données sous-jacentes peuvent changer et que la vue virtualisée doit suivre sa position actuelle en fonction de la clé située en haut de l'écran.
J'ai downvoted cette réponse parce qu'elle est difficile à comprendre. Il a vraiment besoin d'amélioration, surtout en ce qui concerne la langue anglaise. – bitbonk