ListView と GridView のデータ仮想化ListView and GridView data virtualization

  詳しくは、//build/ セッション「Dramatically Increase Performance when Users Interact with Large Amounts of Data in GridView and ListView」 (ユーザーが GridView と ListView で大量のデータを操作するときのパフォーマンスを大幅に向上させる) をご覧ください。Note  For more details, see the //build/ session Dramatically Increase Performance when Users Interact with Large Amounts of Data in GridView and ListView.

データ仮想化によって ListViewGridView のパフォーマンスと起動時間を向上させます。Improve ListView and GridView performance and startup time through data virtualization. UI の仮想化、要素の削減、項目の段階的な更新については、「ListView と GridView の UI の最適化」をご覧ください。For UI virtualization, element reduction, and progressive updating of items, see ListView and GridView UI optimization.

データ仮想化のメソッドは、大きすぎてメモリに一度に格納できないか、すべてを格納する必要がないデータ セットで必要です。A method of data virtualization is needed for a data set that is so large that it cannot or should not all be stored in memory at one time. 最初の部分を (ローカル ディスク、ネットワーク、またはクラウドから) メモリに読み込んで、その部分的なデータ セットに UI の仮想化を適用します。You load an initial portion into memory (from local disk, network, or cloud) and apply UI virtualization to this partial data set. データは、後から段階的に読み込むことも、マスター データ セット内の任意の位置からオンデマンドで読み込むこともできます (ランダム アクセス)。You can later load data incrementally, or from arbitrary points in the master data set (random access), on demand. データ仮想化が適しているかどうかは、多数の要因によって決まります。Whether data virtualization is appropriate for you depends on many factors.

  • データ セットのサイズThe size of your data set
  • 各項目のサイズThe size of each item
  • データ セットのソース (ローカル ディスク、ネットワーク、またはクラウド)The source of the data set (local disk, network, or cloud)
  • アプリの総合的なメモリ消費量The overall memory consumption of your app

  ListView と GridView では、ユーザーがパンやスクロールの操作をすばやく行った場合に一時的なプレースホルダーの視覚効果を表示する機能が既定で有効になることに注意してください。Note  Be aware that a feature is enabled by default for ListView and GridView that displays temporary placeholder visuals while the user is panning/scrolling quickly. これらのプレース ホルダーの視覚効果は、データが読み込まれると項目テンプレートに置き換えられます。As data is loaded, these placeholder visuals are replaced with your item template. この機能は、ListViewBase.ShowsScrollingPlaceholders を false に設定することによって無効にできますが、その場合は、x:Phase 属性を使って項目テンプレートの要素を段階的にレンダリングすることをお勧めします。You can turn the feature off by setting ListViewBase.ShowsScrollingPlaceholders to false, but if you do so then we recommend that you use the x:Phase attribute to progressively render the elements in your item template. 詳しくは、「GridView と ListView の項目を段階的に更新する」をご覧ください。See Update ListView and GridView items progressively.

以降では、段階的なデータ仮想化とランダム アクセスのデータ仮想化の手法について詳しく説明します。Here are more details about the incremental and random-access data virtualization techniques.

段階的なデータ仮想化Incremental data virtualization

段階的なデータ仮想化では、データを連続的にダウンロードします。Incremental data virtualization loads data sequentially. 段階的なデータ仮想化を実行する ListView を使って、数百万の項目のコレクションを表示できますが、最初は 50 個の項目だけが読み込まれます。A ListView that uses incremental data virtualization may be used to view a collection of a million items, but only 50 items are loaded initially. ユーザーがパン/スクロールすると、次の 50 個の項目が読み込まれます。As the user pans/scrolls, the next 50 are loaded. 項目が読み込まれると、スクロール バーのサムはサイズが小さくなります。As items are loaded, the scroll bar's thumb decreases in size. この種のデータ仮想化では、次のインターフェイスを実装するデータ ソース クラスを記述します。For this type of data virtualization you write a data source class that implements these interfaces.

このようなデータ ソースは、継続的に拡張できるメモリ内リストです。A data source like this is an in-memory list that can be continually extended. 項目コントロールは、標準的な IList インデクサーとカウント プロパティを使って項目を要求します。The items control will ask for items using the standard IList indexer and count properties. カウントは、データセットの実際のサイズではなく、ローカルでの項目の数を表す必要があります。The count should represent the number of items locally, not the true size of the dataset.

項目コントロールは、既存のデータの終わりに近づいたときに ISupportIncrementalLoading.HasMoreItems を呼び出します。When the items control gets close to the end of the existing data, it will call ISupportIncrementalLoading.HasMoreItems. true が返された場合は、アドバタイズされた読み込む項目数を渡す ISupportIncrementalLoading.LoadMoreItemsAsync を呼び出します。If you return true, then it will call ISupportIncrementalLoading.LoadMoreItemsAsync passing an advised number of items to load. データの読み込み元 (ローカル ディスク、ネットワーク、またはクラウド) に応じて、アドバタイズされた項目数とは異なる数の項目を読み込むことができます。Depending on where you're loading data from (local disk, network, or cloud), you may choose to load a different number of items than that advised. たとえば、サービスは 50 項目のバッチをサポートしているが、項目コントロールは 10 項目のみを要求している場合、50 項目を読み込むことができます。For example, if your service supports batches of 50 items but the items control only asks for 10 then you can load 50. バックエンドからデータを読み込んでリストに追加した後、INotifyCollectionChanged または IObservableVector<T> 経由で変更通知を発行して、項目コントロールが新しい項目を認識できるようにします。Load the data from your back end, add it to your list, and raise a change notification via INotifyCollectionChanged or IObservableVector<T> so that the items control knows about the new items. さらに、実際に読み込んだ項目の数を返します。Also return a count of the items you actually loaded. アドバタイズされた数よりも少ない項目を読み込むか、項目コントロールが途中でさらにパン/スクロールされた場合は、データ ソースをもう一度呼び出して、さらに項目を読み込むサイクルが続けられます。If you load fewer items than advised, or the items control has been panned/scrolled even further in the interim, then your data source will be called again for more items and the cycle will continue. 詳しくは、Windows 8.1 の XAML データ バインディングのサンプルをダウンロードしてご覧ください。また、Windows 10 アプリでソース コードを再利用することもできます。You can learn more by downloading the XAML data binding sample for Windows 8.1 and re-using its source code in your Windows 10 app.

ランダム アクセスのデータ仮想化Random access data virtualization

ランダム アクセスのデータ仮想化を使うと、データ セット内の任意の位置からデータを読み込むことができます。Random access data virtualization allows loading from an arbitrary point in the data set. ランダム アクセスのデータ仮想化を実行する ListView を、100 万の項目があるコレクションを表示するために使うと、100,000 番目から 100,050 番目の項目を読み込むことができます。A ListView that uses random access data virtualization, used to view a collection of a million items, can load the items 100,000 – 100,050. ユーザーが一覧の先頭に移動すると、コントロールは 1 番目から 50 番目の項目を読み込みます。If the user then moves to the beginning of the list, the control loads items 1 – 50. スクロール バーのサムは、常に ListView に 100 万の項目が含まれていることを示します。At all times, the scroll bar's thumb indicates that the ListView contains a million items. スクロール バーのサムの位置は、表示されている項目がコレクションのデータ セット全体で相対的にどこに位置しているかを示します。The position of the scroll bar's thumb is relative to where the visible items are located in the collection's entire data set. この種のデータ仮想化は、必要なメモリを大幅に減らし、コレクションの読み込み時間を大きく短縮します。This type of data virtualization can significantly reduce the memory requirements and load times for the collection. これを有効にするには、データをオンデマンドで取得し、ローカル キャッシュを管理し、次のインターフェイスを実装するデータ ソース クラスを記述する必要があります。To enable it you need to write a data source class that fetches data on demand and manages a local cache and implements these interfaces.

IItemsRangeInfo は、コントロールが実際に使っている項目の情報を提供します。IItemsRangeInfo provides information on which items the control is actively using. 項目コントロールはビューが変更されるたびにこのメソッドを呼び出し、その中には 次の 2 つの範囲のセットが含まれます。The items control will call this method whenever its view is changing, and will include these two sets of ranges.

  • ビューポート内の項目のセット。The set of items that are in the viewport.
  • 項目コントロールが使う項目で、ビューポートに表示されない可能性がある、仮想化されていない項目のセット。A set of non-virtualized items that the control is using that may not be in the viewport.
    • タッチ パンをスムーズに行えるようにするために項目コントロールが保持している、ビューポートの周囲の項目のバッファー。A buffer of items around the viewport that the items control keeps so that touch panning is smooth.
    • フォーカスが置かれている項目。The focused item.
    • 先頭の項目。The first item.

IItemsRangeInfo を実装することで、データ ソースは、どの項目をフェッチしてキャッシュする必要があり、不要になったキャッシュ データをいつ除去するかがわかります。By implementing IItemsRangeInfo your data source knows what items need to be fetched and cached, and when to prune from the cache data that is no longer needed. IItemsRangeInfo は、ItemIndexRange オブジェクトを使って、コレクション内のインデックスに基づいてオブジェクトのセットを記述します。IItemsRangeInfo uses ItemIndexRange objects to describe a set of items based on their index in the collection. これは、正しくないか安定していない可能性がある項目ポインターを使わないようにするためです。This is so that it doesn't use item pointers, which may not be correct or stable. IItemsRangeInfo は、項目コントロールの状態情報に頼っているため、項目コントロールの 1 つのインスタンスでのみ使われるように設計されています。IItemsRangeInfo is designed to be used by only a single instance of an items control because it relies on state information for that items control. 複数の項目コントロールが同じデータにアクセスする必要がある場合は、それぞれに対してデータ ソースの個別のインスタンスが必要です。If multiple items controls need access to the same data then you will need a separate instance of the data source for each. それらは共通のキャッシュを共有できますが、キャッシュから消去するためのロジックはもっと複雑です。They can share a common cache, but the logic to purge from the cache will be more complicated.

ランダム アクセスのデータ仮想化データ ソースのための基本的な戦略を次に示します。Here's the basic strategy for your random access data virtualization data source.

  • 項目を要求されたときWhen asked for an item
    • メモリ内の項目を利用できる場合はその項目を返します。If you have it available in memory, then return it.
    • 利用できない場合は、null またはプレースホルダー項目を返します。If you don’t have it, then return either null or a placeholder item.
    • 項目に対する要求 (または IItemsRangeInfo からの範囲要求) を使って、どの項目が必要であるかを調べ、バックエンドから項目のデータを非同期的に取得します。Use the request for an item (or the range information from IItemsRangeInfo) to know which items are needed, and to fetch data for items from your back end asynchronously. データを取得した後、INotifyCollectionChanged または IObservableVector<T> 経由で変更通知を発行して、項目コントロールが新しい項目を認識できるようにします。After retrieving the data, raise a change notification via INotifyCollectionChanged or IObservableVector<T> so that the items control knows about the new item.
  • (必要に応じて) 項目コントロールのビューポートが変更されるときに、IItemsRangeInfo の実装を通してどの項目がデータ ソースから必要であるかを識別します。(Optionally) as the items control's viewport changes, identify what items are needed from your data source via your implementation of IItemsRangeInfo.

その他のいつデータ項目を読み込むか、いくつ読み込むか、そしてどの項目をメモリに保持するかは、アプリケーションにまかされます。Beyond that, the strategy for when to load data items, how many to load, and which items to keep in memory is up to your application. いくつかの一般的な考慮事項を次に示します。Some general considerations to keep in mind:

  • データは非同期に要求します。UI スレッドをブロックしないでください。Make asynchronous requests for data; don't block the UI thread.
  • 項目を取得するバッチのサイズのスイート スポットを探します。Find the sweet spot in the size of the batches you fetch items in. ブロックで処理するようにします。Prefer chunky to chatty. 小さな要求を何度も繰り返すほど小さくなく、取得するまで時間がかかりすぎるほど大きくないサイズにします。Not so small that you make too many small requests; not too large that they take too long to retrieve.
  • 同時に保留中にする要求の数を検討します。Consider how many requests you want to have pending at the same time. 簡単なのは一度に 1 つずつ実行することですが、完了までの時間がかかる場合は遅くなりすぎる可能性があります。Performing one at a time is easier, but it may be too slow if turnaround time is high.
  • データの要求を取り消すことができるかどうか。Can you cancel requests for data?
  • ホストされるサービスを使っている場合は、トランザクションごとにコストが発生するかどうか。If using a hosted service, is there a cost per transaction?
  • クエリの結果が変更されるときにサービスによって提供される通知の種類は何か。What kind of notifications are provided by the service when the results of a query are changed? 項目がンデックス 33 に挿入されたことがわかるか。Will you know if an item is inserted at index 33? サービスがキーとオフセットに基づくクエリをサポートする場合は、インデックスだけを使うよりも適している可能性があります。If your service supports queries based on a key-plus-offset, that may be better than just using an index.
  • 項目のプリフェッチをいかにスマートに実行するか。How smart do you want to be in pre-fetching items? 必要な項目を予測するためにスクロールの方向と速度を追跡する予定ですか。Are you going to try and track the direction and velocity of scrolling to predict which items are needed?
  • キャッシュの消去をどの程度積極的に行うか。How aggressive do you want to be in purging the cache? これはメモリとエクスペリエンスのトレードオフです。This is a tradeoff of memory versus experience.