これを解決する簡単な方法は、アイテムをフェッチ/作成するためのアルゴリズムとともに、そのアイテムへの弱い参照を維持する「仮想化コレクション」実装を使用することです。このコレクションのコードはかなり複雑で、必要なすべてのインターフェイスと、ロードされたデータの範囲を効率的に追跡するためのデータ構造がありますが、インデックスに基づいて仮想化されたクラスの部分的な API を次に示します。
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();
}
ここでの内部データ構造は、データ範囲のバランスの取れたツリーであり、各データ範囲には開始インデックスと弱い参照の配列が含まれています。
このクラスは、実際にデータをロードするためのロジックを提供するためにサブクラス化されるように設計されています。仕組みは次のとおりです。
- サブクラスのコンストラクターで、
RecordInsertOrDelete
コレクションの初期サイズを設定するために呼び出されます
- を使用して項目にアクセスする
IList/ICollection/IEnumerable
と、ツリーを使用してデータ項目が検索されます。ツリーで見つかったときに弱い参照があり、その弱い参照がまだライフ オブジェクトを指している場合は、そのオブジェクトが返されます。それ以外の場合は、読み込まれて返されます。
- アイテムをロードする必要がある場合、インデックス範囲は、次/前の既にロードされたアイテムのデータ構造を前後に検索することによって計算され
FetchItems
、サブクラスがアイテムをロードできるように抽象が呼び出されます。
- サブクラスの
FetchItems
実装では、項目がフェッチされRecordFetchedItems
、新しい項目で範囲のツリーを更新するために呼び出されます。隣接するノードをマージしてツリーが大きくなりすぎないようにするには、ある程度の複雑さが必要です。
- サブクラスが外部データの変更の通知を受け取ると、呼び出し
RecordInsertOrDelete
てインデックス追跡を更新できます。これにより、開始インデックスが更新されます。挿入の場合、これは範囲を分割することもあり、削除の場合、1 つまたは複数の範囲を小さく再作成する必要がある場合があります。IList
およびIList<T>
インターフェイスを介してアイテムが追加/削除されるときに、これと同じアルゴリズムが内部的に使用されます。
- この
Cleanup
メソッドはバックグラウンドで呼び出され、範囲のツリーと破棄WeakReferences
可能な範囲全体、およびあまりにもまばらな範囲 (たとえばWeakReference
、1000 スロットの範囲に 1 つだけ)をインクリメンタルに検索します。
FetchItems
ヒューリスティックを使用して一度に複数のアイテムをロードできるように、アンロードされたアイテムの範囲が渡されることに注意してください。このような単純なヒューリスティックは、次の 100 個のアイテムをロードするか、現在のギャップの終わりまでのいずれか早い方をロードします。
を使用すると、WPF の組み込みの仮想化により、 、 などVirtualizingCollection
の適切なタイミングでデータがロードされます。の代わりに。ListBox
ComboBox
VirtualizingStackPanel
StackPanel
a のTreeView
場合、もう 1 つの手順が必要です。HierarchicalDataTemplate
セット内の a MultiBinding
forItemsSource
は、実数にバインドされ、テンプレート化された親にItemsSource
もバインドされます。IsExpanded
のコンバーターは、2 番目の値 (値) が true の場合はMultiBinding
最初の値 ( )を返し、それ以外の場合は null を返します。これにより、ノードを折りたたむと、コレクションの内容へのすべての参照がすぐに削除され、それらをクリーンアップできるようになります。ItemsSource
IsExpanded
TreeView
VirtualizingCollection
インデックスに基づいて仮想化を行う必要はないことに注意してください。ツリー シナリオでは、全か無かの場合があり、リスト シナリオでは、推定カウントを使用し、必要に応じて「開始キー」/「終了キー」メカニズムを使用して範囲を埋めることができます。これは、基になるデータが変更される可能性があり、仮想化されたビューが画面の上部にあるキーに基づいて現在の場所を追跡する必要がある場合に役立ちます。