パフォーマンスの最適化: データ バインディングOptimizing Performance: Data Binding

Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) データ バインディングは、アプリケーションがデータを提示し、データと対話するための簡単で一貫性のある方法を提供します。data binding provides a simple and consistent way for applications to present and interact with data. 要素は、CLR オブジェクトおよび XML の形式のさまざまなデータ ソースのデータにバインドできます。Elements can be bound to data from a variety of data sources in the form of CLR objects and XML.

このトピックでは、データ バインディングのパフォーマンスに関する推奨事項について説明します。This topic provides data binding performance recommendations.

データ バインディングの参照が解決されるしくみHow Data Binding References are Resolved

データ バインディングのパフォーマンスの問題に入る前に、Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) のデータ バインディング エンジンがバインディングのオブジェクト参照をどのように解決するのかを説明します。Before discussing data binding performance issues, it is worthwhile to explore how the Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) data binding engine resolves object references for binding.

Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) のデータ バインディングのソースでは、任意の CLR オブジェクトを使用できます。The source of a Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) data binding can be any CLR object. CLR オブジェクトのプロパティ、サブプロパティ、またはインデクサーにバインドできます。You can bind to properties, sub-properties, or indexers of a CLR object. バインディング参照は、Microsoft .NET Framework のリフレクションまたは ICustomTypeDescriptor を使用して解決されます。The binding references are resolved by using either Microsoft .NET Framework reflection or an ICustomTypeDescriptor. 次に、バインディングのオブジェクト参照を解決するための 3 つの方法について説明します。Here are three methods for resolving object references for binding.

1 つ目は、リフレクションを使用する方法です。The first method involves using reflection. この場合、PropertyInfo オブジェクトを使用してプロパティの属性を検出し、プロパティ メタデータにアクセスします。In this case, the PropertyInfo object is used to discover the attributes of the property and provides access to property metadata. ICustomTypeDescriptor インターフェイスを使用している場合、データ バインディング エンジンはこのインターフェイスを使用してプロパティ値にアクセスします。When using the ICustomTypeDescriptor interface, the data binding engine uses this interface to access the property values. ICustomTypeDescriptor インターフェイスは、オブジェクトに静的なプロパティのセットがない場合に特に便利です。The ICustomTypeDescriptor interface is especially useful in cases where the object does not have a static set of properties.

INotifyPropertyChanged インターフェイスを実装するか、または TypeDescriptor に関連付けられている変更通知を使用することによって、プロパティ変更通知を提供できます。Property change notifications can be provided either by implementing the INotifyPropertyChanged interface or by using the change notifications associated with the TypeDescriptor. ただし、プロパティ変更通知を実装するには INotifyPropertyChanged を使用することをお勧めします。However, the preferred strategy for implementing property change notifications is to use INotifyPropertyChanged.

ソース オブジェクトが CLR オブジェクトでソース プロパティが CLR プロパティの場合、Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) のデータ バインディング エンジンでは、最初にソース オブジェクトでリフレクションを使用して TypeDescriptor を取得した後、PropertyDescriptor を照会する必要があります。If the source object is a CLR object and the source property is a CLR property, the Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) data binding engine has to first use reflection on the source object to get the TypeDescriptor, and then query for a PropertyDescriptor. パフォーマンスの観点から見た場合、このリフレクション操作のシーケンスには非常に時間がかかる可能性があります。This sequence of reflection operations is potentially very time-consuming from a performance perspective.

オブジェクト参照を解決するための 2 つ目の方法は、CLR ソース オブジェクトで INotifyPropertyChanged インターフェイスが実装されていて、ソース プロパティが CLR プロパティである場合に使用されます。The second method for resolving object references involves a CLR source object that implements the INotifyPropertyChanged interface, and a source property that is a CLR property. この場合、データ バインディング エンジンは、ソースの型に対してリフレクションを直接使用して必要なプロパティを取得します。In this case, the data binding engine uses reflection directly on the source type and gets the required property. この方法も最適な方法とは言えませんが、最初の方法よりは作業セットの要件の負荷が小さくなります。This is still not the optimal method, but it will cost less in working set requirements than the first method.

オブジェクト参照を解決するための 3 つ目の方法は、ソース オブジェクトが DependencyObject で、ソース プロパティが DependencyProperty である場合に使用されます。The third method for resolving object references involves a source object that is a DependencyObject and a source property that is a DependencyProperty. この場合、データ バインディング エンジンはリフレクションを使用する必要はありません。In this case, the data binding engine does not need to use reflection. 代わりに、プロパティ エンジンとデータ バインディング エンジンが連携してプロパティ参照を個別に解決します。Instead, the property engine and the data binding engine together resolve the property reference independently. これが、データ バインディングに使用されているオブジェクト参照を解決するための最適な方法です。This is the optimal method for resolving object references used for data binding.

これらの 3 つの方法を使用して 1000 個の TextBlock 要素の Text プロパティのデータ バインディングを行ったときの速度の比較を次の表に示します。The table below compares the speed of data binding the Text property of one thousand TextBlock elements using these three methods.

TextBlock の Text プロパティのバインド先Binding the Text property of a TextBlock バインディング時間 (ミリ秒)Binding time (ms) レンダリング時間 -- バインディングを含む (ミリ秒)Render time -- includes binding (ms)
CLR オブジェクトのプロパティTo a property of a CLR object 115115 314314
INotifyPropertyChanged を実装する CLR オブジェクトのプロパティTo a property of a CLR object which implements INotifyPropertyChanged 115115 305305
DependencyObjectDependencyPropertyTo a DependencyProperty of a DependencyObject. 9090 263263

大きな CLR オブジェクトへのバインディングBinding to Large CLR Objects

何千ものプロパティを持つ 1 つの CLR オブジェクトへのデータ バインディングは、パフォーマンスに大きな影響を及ぼします。There is a significant performance impact when you data bind to a single CLR object with thousands of properties. この影響を最小限に抑えるには、その 1 つのオブジェクトを複数の CLR オブジェクトに分割して、個々のオブジェクトのプロパティの数を減らします。You can minimize this impact by dividing the single object into multiple CLR objects with fewer properties. 次の表では、1 つの大きな CLR オブジェクトへのデータ バインディングと複数の小さなオブジェクトへのデータ バインディングのバインディング時間とレンダリング時間を示します。The table shows the binding and rendering times for data binding to a single large CLR object versus multiple smaller objects.

1000 個の TextBlock オブジェクトのデータ バインディングのバインド先Data binding 1000 TextBlock objects バインディング時間 (ミリ秒)Binding time (ms) レンダリング時間 -- バインディングを含む (ミリ秒)Render time -- includes binding (ms)
1000 個のプロパティを持つ 1 つの CLR オブジェクトTo a CLR object with 1000 properties 950950 12001200
1 つのプロパティを持つ 1000 個の CLR オブジェクトTo 1000 CLR objects with one property 115115 314314

ItemsSource へのバインディングBinding to an ItemsSource

ListBox に表示する従業員リストが保持されている CLR List<T> オブジェクトがあるとします。Consider a scenario in which you have a CLR List<T> object that holds a list of employees that you want to display in a ListBox. この 2 つのオブジェクトの間に対応関係を作成するには、従業員リストを ListBoxItemsSourceプロパティにバインドします。To create a correspondence between these two objects, you would bind your employee list to the ItemsSource property of the ListBox. ここで、グループに新しい従業員が加わったとします。However, suppose you have a new employee joining your group. バインドされた ListBox 値にその新しい従業員を挿入するには、この従業員を従業員リストに追加するだけで、その変更が自動的にデータ バインディング エンジンに認識されると思われがちです。You might think that in order to insert this new person into your bound ListBox values, you would simply add this person to your employee list and expect this change to be recognized by the data binding engine automatically. しかし、実際にはそうはならず、その変更は ListBox に自動的には反映されません。That assumption would prove false; in actuality, the change will not be reflected in the ListBox automatically. これは、CLR の List<T> オブジェクトではコレクション変更イベントが自動的に発生しないからです。This is because the CLR List<T> object does not automatically raise a collection changed event. ListBox に変更を反映するには、従業員リストを再作成し、もう一度 ListBoxItemsSource プロパティに割り当てる必要があります。In order to get the ListBox to pick up the changes, you would have to recreate your list of employees and re-attach it to the ItemsSource property of the ListBox. この解決方法で問題は解決されますが、パフォーマンスへの影響はきわめて大きくなります。While this solution works, it introduces a huge performance impact. ListBoxItemsSource を新しいオブジェクトに割り当てるたびに、ListBox では最初に前の項目が削除され、リスト全体が再生成されます。Each time you reassign the ItemsSource of ListBox to a new object, the ListBox first throws away its previous items and regenerates its entire list. ListBox が複雑な DataTemplate にマップされている場合、パフォーマンスへの影響はさらに大きくなります。The performance impact is magnified if your ListBox maps to a complex DataTemplate.

この問題は、従業員リストを ObservableCollection<T> にすることによってきわめて効率的に解決することができます。A very efficient solution to this problem is to make your employee list an ObservableCollection<T>. ObservableCollection<T> オブジェクトによって変更通知が生成され、それをデータ バインディング エンジンで受け取ることができます。An ObservableCollection<T> object raises a change notification which the data binding engine can receive. このイベントにより、リスト全体を再生成する必要なく、ItemsControl の項目を追加または削除できます。The event adds or removes an item from an ItemsControl without the need to regenerate the entire list.

次の表では、項目を 1 つ追加した場合に ListBox の更新にかかる時間を示します (UI の仮想化はオフ)。The table below shows the time it takes to update the ListBox (with UI virtualization turned off) when one item is added. 1 行目の数字は、CLR の List<T> オブジェクトが ListBox 要素の ItemsSource にバインドされるときの経過時間を表しています。The number in the first row represents the elapsed time when the CLR List<T> object is bound to ListBox element's ItemsSource. 2 行目の数字は、ObservableCollection<T>ListBox 要素の ItemsSource にバインドされるときの経過時間を表しています。The number in the second row represents the elapsed time when an ObservableCollection<T> is bound to the ListBox element's ItemsSource. ObservableCollection<T> のデータ バインディング方法を使用すると時間を大幅に節約できることがわかります。Note the significant time savings using the ObservableCollection<T> data binding strategy.

ItemsSource のデータ バインディングのバインド先Data binding the ItemsSource 1 項目の更新時間 (ミリ秒)Update time for 1 item (ms)
CLR の List<T> オブジェクトTo a CLR List<T> object 16561656
ObservableCollection<T>To an ObservableCollection<T> 2020

IEnumerable ではなく IList を ItemsControl にバインドするBind IList to ItemsControl not IEnumerable

ItemsControl オブジェクトに IList<T> または IEnumerable のどちらをバインドするかを選択できる場合は、IList<T> オブジェクトを選択します。If you have a choice between binding an IList<T> or an IEnumerable to an ItemsControl object, choose the IList<T> object. IEnumerableItemsControl にバインドすると、WPFWPF でラッパー IList<T> オブジェクトが強制的に作成されます。これにより、追加のオブジェクトの不要なオーバーヘッドが発生するため、パフォーマンスに影響します。Binding IEnumerable to an ItemsControl forces WPFWPF to create a wrapper IList<T> object, which means your performance is impacted by the unnecessary overhead of a second object.

データ バインディングのためだけに CLR オブジェクトを XML に変換しないDo not Convert CLR objects to XML Just for Data Binding.

WPFWPF では、XML コンテンツへのデータ バインディングが可能です。ただし、XML コンテンツへのデータ バインディングは、CLR オブジェクトへのデータ バインディングに比べて低速です。allows you to data bind to XML content; however, data binding to XML content is slower than data binding to CLR objects. CLR オブジェクトのデータをデータ バインディングのためだけに XML に変換しないでください。Do not convert CLR object data to XML if the only purpose is for data binding.

関連項目See also