パフォーマンスの最適化 : データ バインディング
更新 : 2007 年 11 月
Windows Presentation Foundation (WPF) データ バインディングは、アプリケーションがデータを提示し、データと対話するための簡単で一貫性のある方法を提供します。要素は、CLR オブジェクトや XML の形式のさまざまなデータ ソースのデータにバインドできます。
このトピックでは、データ バインディングのパフォーマンスに関する推奨事項について説明します。
このトピックには次のセクションが含まれています。
- データ バインディングの参照が解決されるしくみ
- 大きな CLR オブジェクトへのバインディング
- ItemsSource へのバインディング
- IEnumerable ではなく IList を ItemsControl にバインドする
- データ バインディングのためだけに CLR オブジェクトを XML に変換しない
- 関連トピック
データ バインディングの参照が解決されるしくみ
データ バインディングのパフォーマンスの問題に入る前に、Windows Presentation Foundation (WPF) のデータ バインディング エンジンがバインディングのオブジェクト参照をどのように解決するのかを説明します。
Windows Presentation Foundation (WPF) のデータ バインディングでは、任意の CLR オブジェクトをソースとして使用して、CLR オブジェクトのプロパティ、サブプロパティ、またはインデクサにバインドできます。バインディング参照は、Microsoft .NET Framework のリフレクションか ICustomTypeDescriptor を使用して解決されます。次に、バインディングのオブジェクト参照を解決するための 3 つの方法について説明します。
1 つ目は、リフレクションを使用する方法です。この場合は、PropertyInfo オブジェクトを使用してプロパティの属性を検出し、プロパティ メタデータにアクセスします。ICustomTypeDescriptor インターフェイスを使用している場合は、データ バインディング エンジンはこのインターフェイスを使用してプロパティ値にアクセスします。ICustomTypeDescriptor インターフェイスは、オブジェクトに静的なプロパティのセットがない場合に特に便利です。
INotifyPropertyChanged インターフェイスを実装する、または TypeDescriptor に関連付けられている変更通知を使用することによって、プロパティ変更通知を提供できます。ただし、プロパティ変更通知を実装するには INotifyPropertyChanged を使用することをお勧めします。
ソース オブジェクトが CLR オブジェクトでソース プロパティが CLR プロパティの場合、Windows Presentation Foundation (WPF) のデータ バインディング エンジンでは、最初にソース オブジェクトでリフレクションを使用して TypeDescriptor を取得してから PropertyDescriptor を照会する必要があります。パフォーマンスの観点から見た場合、このリフレクション操作のシーケンスには時間がかかる可能性があります。
オブジェクト参照を解決するための 2 つ目の方法は、ソース オブジェクトが INotifyPropertyChanged インターフェイスを実装する CLR オブジェクトで、ソース プロパティが CLR プロパティの場合に使用されます。この場合、データ バインディング エンジンは、ソースの型に対してリフレクションを直接使用して必要なプロパティを取得します。この方法も最適な方法とは言えませんが、最初の方法よりは作業セットの要件の負荷が小さくなります。
オブジェクト参照を解決するための 3 つ目の方法は、ソース オブジェクトが DependencyObject で、ソース プロパティが DependencyProperty の場合に使用されます。この場合、データ バインディング エンジンはリフレクションを使用する必要はありません。代わりに、プロパティ エンジンとデータ バインディング エンジンが連携してプロパティ参照を個別に解決します。これが、データ バインディングに使用されているオブジェクト参照を解決するための最適な方法です。
これらの方法を使用して 1000 個の TextBlock 要素の Text プロパティのデータ バインディングを行ったときの速度の比較を次の表に示します。
TextBlock の Text プロパティのバインド先 |
バインディング時間 (ミリ秒) |
レンダリング時間 -- バインディングを含む (ミリ秒) |
---|---|---|
CLR オブジェクトのプロパティ |
115 |
314 |
INotifyPropertyChanged を実装する CLR オブジェクトのプロパティ |
115 |
305 |
90 |
263 |
大きな CLR オブジェクトへのバインディング
何千ものプロパティを持つ 1 つの CLR オブジェクトへのデータ バインディングは、パフォーマンスに大きな影響を及ぼします。この影響を最小限に抑えるには、その 1 つのオブジェクトを複数の CLR オブジェクトに分割して、個々のオブジェクトのプロパティの数を減らします。次の表は、1 つの大きな CLR オブジェクトへのデータ バインディングと複数の小さなオブジェクトへのデータ バインディングのバインディング時間とレンダリング時間を示しています。
1000 個の TextBlock オブジェクトのデータ バインディングのバインド先 |
バインディング時間 (ミリ秒) |
レンダリング時間 -- バインディングを含む (ミリ秒) |
---|---|---|
1000 個のプロパティを持つ 1 つの CLR オブジェクト |
950 |
1200 |
1 つのプロパティを持つ 1000 個の CLR オブジェクト |
115 |
314 |
ItemsSource へのバインディング
ListBox に表示する従業員リストを保持する CLRList<T> オブジェクトがあるとします。この 2 つのオブジェクトの間に対応関係を作成するには、従業員リストを ListBox の ItemsSource プロパティにバインドします。ここで、グループに新しい従業員が加わったとします。バインドされた ListBox 値にその新しい従業員を挿入するには、の従業員を従業員リストに追加するだけで、その変更が自動的にデータ バインディング エンジンに認識されると思われがちです。しかし、実際にはそうはならず、その変更は ListBox に自動的には反映されません。これは、CLRList<T> オブジェクトはコレクション変更イベントを自動的に発生させないからです。ListBox に変更を反映するには、従業員リストを再作成し、再度 ListBox の ItemsSource プロパティに割り当てる必要があります。この解決方法で問題は解決されますが、パフォーマンスへの影響はきわめて大きくなります。ListBox の ItemsSource を新しいオブジェクトに割り当てるたびに、ListBox はまず前の項目を削除し、その後にリスト全体を再生成します。ListBox が複雑な DataTemplate にマップされている場合、パフォーマンスへの影響はさらに大きくなります。
この問題は、従業員リストを ObservableCollection<T> にすることによってきわめて効率的に解決することができます。ObservableCollection<T> オブジェクトは変更通知を発生させるため、それをデータ バインディング エンジンで受け取ることができます。このイベントにより、リスト全体を再生成する必要なく、ItemsControl の項目を追加または削除できます。
次の表は、項目を 1 つ追加した場合に ListBox の更新にかかる時間を示しています (UI の仮想化はオフ)。1 行目の数字は、CLR List<T> オブジェクトが ListBox 要素の ItemsSource にバインドされている場合の経過時間を表しています。2 行目の数字は、ObservableCollection<T> が ListBox 要素の ItemsSource にバインドされている場合の経過時間を表しています。ObservableCollection<T> のデータ バインディング方法を使用すると時間を大幅に節約できることがわかります。
ItemsSource のデータ バインディングのバインド先 |
1 項目の更新時間 (ミリ秒) |
---|---|
CLRList<T> オブジェクト |
1656 |
20 |
IEnumerable ではなく IList を ItemsControl にバインドする
ItemsControl オブジェクトに IList<T> または IEnumerable のどちらもバインドできる場合は、IList<T> オブジェクトを使用してください。IEnumerable を ItemsControl にバインドすると、WPF でラッパー IList<T> オブジェクトが作成されます。これにより、追加のオブジェクトの不要なオーバーヘッドが発生するため、パフォーマンスに影響します。
データ バインディングのためだけに CLR オブジェクトを XML に変換しない
WPF では、XML コンテンツへのデータ バインディングが可能です。ただし、XML コンテンツへのデータ バインディングは、CLR オブジェクトへのデータ バインディングに比べて低速です。CLR オブジェクトのデータをデータ バインディングのためだけに XML に変換しないでください。