データ バインディングの概要

Windows Presentation Foundation (WPF) データ バインディングは、アプリケーションがデータを提示し、データと対話するための簡単で一貫性のある方法を提供します。 要素は、common language runtime (CLR) オブジェクトや XML の形式のさまざまなデータ ソースのデータにバインドできます。 Button などの ContentControl と、ListBoxListView などの ItemsControl には、単一のデータ項目やデータ項目のコレクションのスタイルを柔軟に設定できるようにするための機能が組み込まれています。 データの最上位に並べ替え、フィルター、およびグループ ビューを生成できます。

WPF のデータ バインディング機能には、従来のモデルに比べていくつかの利点があります。たとえば、データ バインディングをネイティブにサポートするさまざまなプロパティ、データの柔軟な UI 表現、ビジネス ロジックと UI の明確な分離などです。

このトピックでは、最初に WPF データ バインディングの基本的な概念について説明し、次に Binding クラスの使用方法とデータ バインディングのその他の機能について説明します。

このトピックは、次のセクションで構成されています。

  • データ バインディングとは
  • 基本的なデータ バインディングの概念
  • バインディングの作成
  • データ変換
  • コレクションへのバインド
  • データ テンプレート
  • データの検証
  • デバッグ機構
  • 関連トピック

データ バインディングとは

データ バインディングとは、アプリケーションの UI とビジネス ロジックを関連付けるプロセスです。 バインディングが適切に設定されており、データが適切な通知を提供する場合、データの値が変更されると、データにバインドされている要素にその変更が自動的に反映されます。 また、データ バインディングは、要素内のデータの外部表現が変更された場合、基になるデータが自動的に更新されてその変更が反映されるということも意味します。 たとえば、ユーザーが TextBox 要素の値を編集した場合、基になるデータの値が自動的に更新されてその変更が反映されます。

データ バインディングの代表的な用途としては、サーバーまたはローカルの構成データをフォームなどの UI コントロールに配置することです。 WPF では、この概念が、さまざまなプロパティとさまざまなデータ ソースのバインディングにまで拡張されています。 WPF では、要素の依存関係プロパティを、CLR オブジェクト (ADO.NET オブジェクトや、Web サービスおよび Web プロパティに関連するオブジェクトなど) や XML データにバインドできます。

データ バインディングの例としては、次のアプリケーション UI をデータ バインディングのデモからダウンロードして参照してください。

データ バインディングのサンプルのスクリーンショット

上の図は、オークション品目の一覧を表示するアプリケーションの UI です。 このアプリケーションでは、次のデータ バインディングの機能を示します。

  • ListBox のコンテンツは、AuctionItem オブジェクトのコレクションにバインドされています。 AuctionItem オブジェクトには、DescriptionStartPriceStartDateCategorySpecialFeatures などのプロパティがあります。

  • ListBox に表示されるデータ (AuctionItem オブジェクト) は、各品目の説明と現在の価格が表示されるようにテンプレート化されています。 このテンプレート化には DataTemplate を使用しています。 また、各品目の外観は、表示されている AuctionItemSpecialFeatures 値に依存します。 AuctionItemSpecialFeatures 値が Color の場合は、品目の境界線が青色になります。 値が Highlight の場合は、品目の境界線がオレンジ色になり、項目に星が表示されます。 データ テンプレートに関する情報については、「データ テンプレート」セクションで説明します。

  • ユーザーは、画面上にある CheckBox を使用して、データのグループ化、フィルター処理、または並べ替えを行うことができます。 上のイメージでは、[Group by category] と [Sort by category and date] の CheckBox がオンになっています。 商品のカテゴリを基にデータがグループ化されており、カテゴリ名がアルファベット順になっています。 このイメージからはわかりづらいですが、各カテゴリ内の品目は開始日で並べ替えられています。 この並べ替えにはコレクション ビューを使用しています。 コレクション ビューについては、「コレクションへのバインディング」セクションで説明します。

  • ユーザーが品目を選択すると、その品目の詳細が ContentControl に表示されます。 これはマスター詳細シナリオと呼ばれます。 この種のバインディング シナリオに関する情報については、「マスター詳細シナリオ」セクションで説明します。

  • StartDate プロパティの型は DateTime です。この型は、ミリ秒までの時刻を含む日付を返します。 このアプリケーションでは、カスタム コンバーターが使用されているため、短い形式の日付文字列が表示されます。 これらのコンバーターに関する情報については、「データ変換」セクションで説明します。

ユーザーが [Add Product] ボタンをクリックすると、次のフォームが表示されます。

[Add Product Listing] ページ

ユーザーは、フォーム内のフィールドを編集し、簡単なプレビュー ペインと詳細なプレビュー ペインで商品一覧をプレビューしてから、[submit] をクリックして新しい商品一覧を追加することができます。 既存のグループ化、フィルター処理、および並べ替えの機能が新しいエントリに適用されます。 この場合、上のイメージに入力した品目は、Computer カテゴリ内の 2 番目の品目として表示されます。

このイメージでは、Start Date TextBox で指定されている検証ロジックが示されていません。 ユーザーが無効な日付 (無効な形式または過去の日付) を入力した場合は、日付が無効であることが、ToolTipTextBox の横の赤い感嘆符でユーザーに通知されます。 検証ロジックの作成方法については、「データの検証」セクションで説明します。

上であらましを述べたさまざまなデータ バインディング機能について説明する前に、次のセクションでは、まず WPF データ バインディングを理解するのに不可欠な基本的概念について説明します。

基本的なデータ バインディングの概念

ここでは、次の項目について説明します。

  • データ フローの方向
  • ソースの更新の要因

バインドする要素の種類やデータ ソースの性質に関係なく、各バインディングは常に、次の図に示されているモデルに従って行われます。

基本的なデータ バインディング ダイアグラム

上の図に示されているように、データ バインディングは、基本的にバインディング ターゲットとバインディング ソース間のブリッジです。 この図は、次の基本的な WPF データ バインディングの概念を示しています。

  • 通常、各バインディングには、バインディング ターゲット オブジェクト、ターゲット プロパティ、バインディング ソース、および使用するバインディング ソース内の値へのパスの 4 つのコンポーネントがあります。 たとえば、TextBox の内容を Employee オブジェクトの Name プロパティにバインドする場合、対象オブジェクトは TextBox、対象プロパティは Text プロパティ、使用する値は Name、ソース オブジェクトは Employee オブジェクトになります。

  • ターゲット プロパティには、依存関係プロパティを指定する必要があります。 UIElement のほとんどのプロパティは依存関係プロパティであり、読み取り専用のものを除くほとんどの依存関係プロパティは、既定でデータ バインディングをサポートします (DependencyObject 型だけが依存関係プロパティ、および DependencyObject から派生したすべての UIElement を定義できます)。

  • 図には示されていませんが、バインディング ソース オブジェクトはカスタムの CLR オブジェクトであってもかまいません。 WPF のデータ バインディングでは、CLR オブジェクトおよび XML の形式のデータをサポートします。 いくつか例を挙げると、バインディング ソースは UIElement、任意のリスト オブジェクト、ADO.NET データまたは Web サービスに関連付けられた CLR オブジェクト、または XML データを含む XmlNode のいずれでもかまいません。 詳細については、「バインディング ソースの概要」を参照してください。

バインディングを確立する場合、バインディング ターゲットをバインディング ソースにバインドします。software development kit (SDK) の他のトピックに目を通す際には、この点を覚えておくことが重要です。 たとえば、データ バインディングを使用して基になる XML データを ListBox に表示する場合は、ListBox を XML データにバインドします。

バインディングを確立するには、Binding オブジェクトを使用します。 以降では、Binding オブジェクトに関連するさまざまな概念と、このオブジェクトのいくつかのプロパティおよび使用方法について説明します。

データ フローの方向

既に説明し、上の図の矢印でも示したように、バインディングのデータ フローには、バインディング ターゲットからバインディング ソースへのフロー (ユーザーが TextBox の値を編集したときのソース値の変更など) と、バインディング ソースが適切な通知を提供する場合のバインディング ソースからバインディング ターゲットへのフロー (バインディング ソースの変更による TextBox の更新など) があります。

アプリケーションでユーザーによるデータの変更を可能にし、その変更をソース オブジェクトに反映できるようにする必要が生じる場合があります。 あるいは、ユーザーがソース データを更新できないようにする必要が生じることもあります。 Binding オブジェクトの Mode プロパティを設定することで、こうした動作を制御することができます。 次の図は、さまざまな種類のデータ フローを示しています。

データ バインディング データ フロー

  • OneWay バインディングでは、ソース プロパティが変更されると、ターゲット プロパティも自動的に更新されます。ただし、ターゲット プロパティの変更はソース プロパティに反映されません。 この型のバインディングは、バインドされているコントロールが暗黙的な読み取り専用の場合に適しています。 たとえば、株価情報などのソースにバインドする場合です。また、ターゲット プロパティには、データ バインドされたテーブルの背景色などの変更用コントロール インターフェイスがない可能性もあります。 ターゲット プロパティの変更を監視する必要がない場合は、OneWay バインディング モードを使うことにより、TwoWay バインディング モードのオーバーヘッドを回避できます。

  • TwoWay バインディングでは、ソース プロパティまたはターゲット プロパティのどちらか一方が変更されると、もう一方も自動的に更新されます。 この型のバインディングは、編集可能なフォームや完全対話型の UI シナリオに適しています。 ほとんどのプロパティは既定で OneWay バインディングになります。ただし、一部の依存関係プロパティ (一般的に、TextBoxText プロパティや CheckBoxIsChecked プロパティなど、ユーザーが編集できるコントロールのプロパティ) は既定で TwoWay バインディングになります。 依存関係プロパティが既定で一方向または双方向のいずれであるかをプログラムにより判断する 1 つの方法は、GetMetadata を使用してプロパティのプロパティ メタデータを取得してから、BindsTwoWayByDefault プロパティのブール値を確認することです。

  • OneWayToSourceOneWay バインディングの逆方向のバインディングです。このバインディングでは、ターゲット プロパティが変更されると、ソース プロパティが更新されます。 シナリオの例の 1 つには、UI からのソース値のみを再評価する必要がある場合があります。

  • 図に示されていない OneTime バインディングでは、ソース プロパティによってターゲット プロパティが初期化されます。ただし、それ以降の変更は反映されません。 これは、データ コンテキストまたはデータ コンテキスト内のオブジェクトが変更された場合、その変更はターゲット プロパティに反映されないということを意味します。 この型のバインディングは、現在の状態のスナップショットを使用した方がよい場合や、データが完全に静的である場合に適しています。 また、ソース プロパティの値を使用してターゲット プロパティを初期化するときにデータ コンテキストが事前にわからない場合にも、この型のバインディングは便利です。 基本的に、この型のバインディングは、ソース値が変わらない場合にパフォーマンスを向上させる OneWay バインディングを簡易化したものです。

ソースの変更を検出するには (OneWay および TwoWay バインディングが該当)、ソースで INotifyPropertyChanged などの適切なプロパティの変更通知機構を実装する必要があります。 INotifyPropertyChanged の実装の例については、「方法 : プロパティの変更通知を実装する」を参照してください。

バインディング モードの詳細とバインディングの方向を指定する方法の例については、Mode プロパティのページを参照してください。

ソースの更新の要因

TwoWay または OneWayToSource のバインディングでは、対象プロパティ内の変更をリッスンして、ソースに反映させます。 これを、ソースの更新と呼びます。 たとえば、TextBox のテキストを編集して、基になるソース値を変更できます。 前のセクションで説明したように、データ フローの方向は、バインディングの Mode プロパティの値によって決定されます。

では、ソース値はテキストの編集中に更新されるのでしょうか。それとも、テキストの編集を終了し、マウスのポインターを TextBox から離した後に更新されるのでしょうか。 ソースの更新の要因は、バインディングの UpdateSourceTrigger プロパティによって決定されます。 次の図の点線の右矢印は、UpdateSourceTrigger プロパティの役割を示しています。

UpdateSourceTrigger ダイアグラム

UpdateSourceTrigger 値が PropertyChanged の場合、TwoWay バインディングまたは OneWayToSource バインディングの右矢印が指す値は、ターゲット プロパティが変更された直後に更新されます。 一方、UpdateSourceTrigger 値が LostFocus の場合、この値が新しい値に更新されるのは、ターゲット プロパティがフォーカスを失ったときだけです。

Mode プロパティと同様に、依存関係プロパティの既定の UpdateSourceTrigger 値は、依存関係プロパティごとに異なります。 ほとんどの依存関係プロパティの既定値は PropertyChanged であるのに対し、Text プロパティの既定値は LostFocus です。 つまり、通常はターゲット プロパティの変更時にソースが更新されます。こうした動作は、CheckBox などの単純なコントロールに適しています。 ただし、テキスト フィールドの場合、各キーストロークの後に更新を行うと、パフォーマンスが低下する可能性があり、ユーザーは新しい値をコミットする前に BackSpace キーを使用して入力エラーを修正するという通常の操作ができなくなります。 Text プロパティの既定値が、PropertyChanged ではなく LostFocus に設定されているのはこのためです。

依存関係プロパティの既定の UpdateSourceTrigger 値を確認する方法については、UpdateSourceTrigger プロパティのページを参照してください。

次の表は、各 UpdateSourceTrigger 値のシナリオ例を示しています。これらのシナリオでは、TextBox を例として使用しています。

UpdateSourceTrigger 値

ソース値が更新されるタイミング

TextBox のシナリオ例

LostFocus (TextBox.Text の既定値)

TextBox コントロールがフォーカスを失ったとき

検証ロジックに関連付けられている TextBox (「データの検証」セクションを参照)

PropertyChanged

TextBox に入力したとき

チャット ルーム ウィンドウの TextBox コントロール

Explicit

アプリケーションが UpdateSource を呼び出したとき

編集可能なフォームの TextBox コントロール (ユーザーが送信ボタンをクリックした場合のみソース値を更新する)

例については、「方法 : TextBox テキストでソースを更新するタイミングを制御する」を参照してください。

バインディングの作成

ここでは、次の項目について説明します。

  • バインディング ソースの指定
  • 値へのパスの指定
  • Binding と BindingExpression

前のセクションで説明したいくつかの概念を要約するために、Binding を使用してバインディングを設定します。通常、各バインディングには、バインディング ターゲット、ターゲット プロパティ、バインディング ソース、および使用するソース値へのパスの 4 つのコンポーネントがあります。 このセクションでは、バインディングの設定方法について説明します。

次に例を示します。この例では、SDKSample 名前空間内で定義されている MyData という名前のクラスをバインディング ソース オブジェクトに設定しています。 デモの目的で、MyData クラスは ColorName という名前の文字列プロパティを持っており、プロパティの値は "Red" に設定されています。 したがって、この例では赤い背景のボタンが作成されます。

<DockPanel
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:c="clr-namespace:SDKSample">
  <DockPanel.Resources>
    <c:MyData x:Key="myDataSource"/>
  </DockPanel.Resources>
  <DockPanel.DataContext>
    <Binding Source="{StaticResource myDataSource}"/>
  </DockPanel.DataContext>
  <Button Background="{Binding Path=ColorName}"
          Width="150" Height="30">I am bound to be RED!</Button>
</DockPanel>

バインディング宣言構文の詳細とコードでバインディングを設定する方法の例については、「バインディング宣言の概要」を参照してください。

上の基本的な図にこの例を適用すると、次のような図が得られます。 Background プロパティは既定で OneWay バインディングをサポートするため、これは OneWay バインディングになります。

データ バインディング ダイアグラム

ColorName プロパティは文字列型で Background プロパティは Brush 型であるにもかかわらず、このバインディングが正常に機能することに疑問を持たれるかもしれません。 これは既定の型変換が実行されるためです。型変換については「データ変換」セクションで説明します。

バインディング ソースの指定

前の例では、DockPanel 要素の DataContext プロパティを設定することで、バインディング ソースを指定しています。 次に、Button は、その親要素である DockPanel から DataContext 値を継承します。 繰り返しますが、バインディング ソース オブジェクトは、バインディングの 4 つの必須コンポーネントの 1 つです。 したがって、バインディング ソース オブジェクトが指定されていない場合、バインディングは機能しません。

バインディング ソース オブジェクトを指定する方法はいくつかあります。 親要素の DataContext プロパティを使用する方法は、複数のプロパティを同じソースにバインドする場合に役立ちます。 ただし、個々のバインディング宣言でバインディング ソースを指定する方が適している場合もあります。 前の例の場合は、DataContext プロパティを使用する代わりに、ボタンのバインディング宣言で直接 Source プロパティを設定することでバインディング ソースを指定できます。次に例を示します。

<DockPanel.Resources>
  <c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<Button Width="150" Height="30"
        Background="{Binding Source={StaticResource myDataSource},
                             Path=ColorName}">I am bound to be RED!</Button>

DataContext プロパティを要素に直接設定したり、DataContext 値を先祖から継承したり (最初の例のボタンなど)、BindingSource プロパティの設定でバインディング ソースを明示的に指定する方法以外に、ElementName プロパティまたは RelativeSource プロパティを使用してバインディング ソースを指定する方法もあります。 ElementName プロパティは、アプリケーション内の他の要素にバインドする場合 (スライダーを使用してボタンの幅を調整する場合など) に役立ちます。 RelativeSource プロパティは、ControlTemplate または Style でバインディングを指定する場合に役立ちます。 詳細については、「方法 : バインディング ソースを指定する」を参照してください。

値へのパスの指定

バインディング ソースがオブジェクトである場合は、Path プロパティを使用して、バインディングで使用する値を指定します。 XML データにバインドする場合は、XPath プロパティを使用して値を指定します。 場合によっては、データが XML であっても Path プロパティを使用できます。 たとえば、(XPath クエリの結果として) 返された XmlNode の Name プロパティにアクセスする場合は、XPath プロパティに加えて Path プロパティを使用する必要があります。

構文の詳細および例については、Path プロパティと XPath プロパティのページを参照してください。

使用する値への Path はバインディングの 4 つの必須コンポーネントの 1 つであると強調しましたが、オブジェクト全体にバインドするシナリオでは、使用する値はバインディング ソース オブジェクトと同一になります。 このような場合には、Path を指定する必要はありません。 次に例を示します。

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

上の例では、空のバインディング構文 ({Binding}) を使用しています。 この場合、ListBox は、親の DockPanel 要素から DataContext を継承します (この例には示されていません)。 パスを指定しない場合、既定でオブジェクト全体にバインドされます。 つまり、この例では、ItemsSource プロパティをオブジェクト全体にバインドしているため、パスが省略されたことになります (詳細については、「コレクションへのバインド」セクションを参照してください)。

このシナリオは、コレクションにバインドする場合だけでなく、オブジェクトの単一のプロパティの代わりにオブジェクト全体にバインドする場合にも役立ちます。 たとえば、ソース オブジェクトが文字列型で、その文字列自体にバインドする場合などです。 もう 1 つの一般的なシナリオは、複数のプロパティを持つオブジェクトに要素をバインドする場合です。

カスタム ロジックを適用して、バインドしたターゲット プロパティでデータが有効になるようにしなければならない場合があることに注意してください。 カスタム ロジックは、カスタム コンバーターの形式になることがあります (既定の型変換が存在しない場合)。 コンバーターの詳細については、「データ変換」を参照してください。

Binding と BindingExpression

データ バインディングのその他の機能と使用方法について説明する前に、BindingExpression クラスについて説明した方がよいでしょう。 前のセクションで説明したように、Binding クラスは、バインディング宣言で使用する高レベルのクラスです。Binding クラスは、バインディングの特性を指定するためのさまざまなプロパティを提供します。 関連クラスである BindingExpression クラスは、ソースとターゲットとの関連付けを維持する、基になるオブジェクトです。 バインディングには、複数のバインディング式で共有可能な情報がすべて含まれています。 BindingExpression は、共有できないインスタンス式であり、Binding に関するインスタンス情報がすべて含まれています。

次に例を示します。myDataObjectMyData クラスのインスタンス、myBindingBinding ソース オブジェクト、MyData クラスは MyDataProperty という名前の文字列プロパティを含む定義済みクラスです。 この例では、TextBlock のインスタンスである mytext のテキスト コンテンツを、MyDataProperty にバインドします。

Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
//make a new source
  MyData myDataObject = new MyData(DateTime.Now);      
  Binding myBinding = new Binding("MyDataProperty");
  myBinding.Source = myDataObject;
  myText.SetBinding(TextBlock.TextProperty, myBinding);

同じ myBinding オブジェクトを使用して、他のバインディングを作成できます。 たとえば、myBinding オブジェクトを使用して、チェック ボックスのテキスト コンテンツを MyDataProperty にバインドできます。 このシナリオには、myBinding オブジェクトを共有する BindingExpression のインスタンスが 2 つあります。

BindingExpression オブジェクトは、データ バインドされたオブジェクト上の GetBindingExpression を呼び出した結果の戻り値を使用して取得できます。 以下のトピックでは、BindingExpression クラスの使用方法の一部を示します。

データ変換

前の例では、ボタンの Background プロパティが "Red" という値を持つ文字列プロパティにバインドされているため、ボタンの色が赤になります。 これが機能するのは、文字列値を Brush に変換する型コンバーターが Brush 型に存在するためです。

この情報を「バインディングの作成」セクションの図に追加すると、次のような図が得られます。

データ バインディング ダイアグラム

では、文字列型のプロパティの代わりに、Color 型の Color プロパティがバインディング ソース オブジェクトにある場合はどうなるのでしょうか。 この場合、バインディングを機能させるには、最初に Color プロパティの値を、Background で受け入れられる値に変換する必要があります。 次の例に示すように、IValueConverter インターフェイスを実装して、カスタム コンバーターを作成する必要があります。

    <ValueConversion(GetType(Color), GetType(SolidColorBrush))>
    Public Class ColorBrushConverter
        Implements IValueConverter
        Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
            Dim color As Color = CType(value, Color)
            Return New SolidColorBrush(color)
        End Function

        Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
            Return Nothing
        End Function
    End Class
[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

詳細については、IValueConverter のリファレンス ページを参照してください。

ここでは、既定の変換の代わりにカスタム コンバーターを使用します。したがって、上の図は次のようになります。

データ バインディング ダイアグラム

繰り返しますが、バインディング ターゲットの型に型コンバーターが存在していれば、既定の変換を使用できる場合があります。 この動作は、ターゲットで使用できる型コンバーターによって異なります。 不確かな場合は、独自のコンバーターを作成してください。

型コンバーターを実装することが理にかなっている代表的なシナリオとして、以下が挙げられます。

  • カルチャに応じてデータを異なる形式で表示する必要がある場合。 たとえば、特定のカルチャで使用される値や標準を基に、通貨コンバーターやカレンダーの日付/時刻コンバーターを実装できます。

  • 使用中のデータが必ずしもプロパティのテキスト値を変更するためのものではなく、イメージのソースや表示テキストの色またはスタイルなど、その他の値を変更するためのものである場合。 この場合は、コンバーターを使用して、不適切と思われるプロパティのバインディング (テキスト フィールドとテーブル セルの Background プロパティのバインディングなど) を変換できます。

  • 複数のコントロールまたはコントロールの複数のプロパティを同じデータにバインドする場合。 この場合、同じバインディングをソース情報として使用したまま、プライマリ バインディングでテキストを表示し、その他のバインディングで表示に関する特定の問題を処理することができます。

  • ターゲット プロパティにバインディングのコレクションが含まれる MultiBinding (これについてはまだ説明していません) を使用する場合。 MultiBinding の場合は、カスタム IMultiValueConverter を使用して、バインディングの値から最終的な値を生成します。 たとえば、同じまたは異なるバインディング ソース オブジェクトの値である赤、青、緑の値から色の値を計算できます。 例および詳細については、MultiBinding クラスのページを参照してください。

コレクションへのバインド

ここでは、次の項目について説明します。

  • コレクションを実装する方法
  • コレクション ビュー

バインディング ソース オブジェクトは、プロパティにデータが含まれる単一のオブジェクト、または (データベースへのクエリの結果など) 通常はグループ化されるポリモーフィック オブジェクトのデータ コレクションのいずれかとして処理することができます。 これまでは単一オブジェクトへのバインディングについてのみ説明してきましたが、データ コレクションへのバインディングも一般的なシナリオです。 たとえば、一般的なシナリオでは、「データ バインディングとは」セクションで示したアプリケーションのように、ListBoxListViewTreeView などの ItemsControl を使用してデータ コレクションを表示します。

幸いなことに、上の基本的な図を引き続き適用できます。 ItemsControl をコレクションにバインドしている場合、この図は次のようになります。

データ バインディング ItemsControl ダイアグラム

この図に示されているように、コレクション オブジェクトに ItemsControl をバインドする場合、使用するプロパティは ItemsSource プロパティです。 ItemsSource プロパティは、ItemsControl のコンテンツと考えることができます。 ItemsSource プロパティは、既定では、OneWay バインディングをサポートします。したがって、バインディングは OneWay になることに注意してください。

コレクションを実装する方法

IEnumerable インターフェイスを実装するすべてのコレクションを列挙できます。 ただし、コレクションへの挿入や削除によって UI が自動的に更新されるように動的バインディングを設定するには、コレクションで INotifyCollectionChanged インターフェイスを実装する必要があります。 このインターフェイスは、基になるコレクションが変更されるたびに発生するイベントを公開します。

WPF は、INotifyCollectionChanged インターフェイスを公開するデータ コレクションの組み込み実装である ObservableCollection<T> クラスを提供します。 ソース オブジェクトからターゲットへのデータ値の転送を完全にサポートするためには、バインド可能なプロパティをサポートするコレクション内の各オブジェクトに INotifyPropertyChanged インターフェイスも実装する必要があります。 詳細については、「バインディング ソースの概要」を参照してください。

独自のコレクションを実装する前に、ObservableCollection<T> または既存のコレクション クラスのいずれか (特に List<T>Collection<T>BindingList<T> など) の使用を検討します。 高度なシナリオがあり、独自のコレクションを実装する場合は、IList の使用を検討してください。これにより、インデックスによって個別にアクセスできるオブジェクトの非ジェネリック コレクションが利用可能になり、最適なパフォーマンスが実現されます。

コレクション ビュー

ItemsControl をデータ コレクションにバインドすると、データの並べ替え、フィルター処理、またはグループ化が必要になる場合があります。 これらの処理を実行するには、ICollectionView インターフェイスを実装したクラスであるコレクション ビューを使用します。

ここでは、次の項目について説明します。

  • コレクション ビューとは
  • ビューを作成する方法
  • 並べ替え
  • フィルター処理
  • グループ化
  • 現在の項目のポインター
  • マスター詳細シナリオ

コレクション ビューとは

コレクション ビューは、基になるソース コレクションそのものを操作せずに、並べ替え、フィルター、およびグループ クエリに基づいてソース コレクションを移動および表示できるようにする、バインディング ソース コレクションの最上位層です。 また、コレクション ビューは、コレクション内の現在の項目へのポインターを維持します。 ソース コレクションが INotifyCollectionChanged インターフェイスを実装している場合は、CollectionChanged イベントによって発生した変更がビューに反映されます。

ビューは基になるソース コレクションを変更しないため、各ソース コレクションは関連付けられた複数のビューを持つことができます。 たとえば、Task オブジェクトのコレクションを持つことができます。 ビューを使用することにより、同じデータを異なる方法で表示できます。 たとえば、優先順位で並べ替えたタスクをページの左側に表示し、領域ごとにグループ化されたタスクをページの右側に表示できます。

ビューを作成する方法

ビューを作成および使用する方法の 1 つは、ビュー オブジェクトを直接インスタンス化し、それをバインディング オブジェクトとして使用することです。 たとえば、「データ バインディングとは」セクションで示したデータ バインディングのデモ アプリケーションを参照してください。 このアプリケーションは、ListBox をデータ コレクションに直接バインドするのではなく、データ コレクション上のビューにバインドするように実装されています。 次の例は、データ バインディングのデモ アプリケーションから抽出したものです。 CollectionViewSource クラスは、CollectionView から継承するクラスの Extensible Application Markup Language (XAML) プロキシです。 この例では、現在のアプリケーション オブジェクトの AuctionItems コレクション (ObservableCollection<T> 型) に、ビューの Source をバインドしています。

<Window.Resources>


...


<CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />


...


</Window.Resources>

次に、listingDataView リソースを、アプリケーション内の要素 (ListBox など) のバインディング ソースに設定します。

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
    ItemsSource="{Binding Source={StaticResource listingDataView}}">


...


</ListBox>

同じコレクションに対して別のビューを作成するには、別の CollectionViewSource インスタンスを作成し、そのインスタンスに異なる x:Key 名を付けます。

次の表は、既定のコレクション ビューとして作成されるビュー データの型、またはソース コレクションの型に基づく CollectionViewSource で作成されるビュー データの型を示しています。

ソース コレクションの型

コレクション ビューの型

備考

IEnumerable

CollectionView に基づく内部型

項目をグループ化できません。

IList

ListCollectionView

最も高速です。

IBindingList

BindingListCollectionView

既定のビューの使用

コレクション ビューを作成および使用する 1 つの方法は、コレクション ビューをバインディング オブジェクトとして指定することです。 また、WPF は、バインディング ソースとして使用されるすべてのコレクション用の既定のコレクション ビューを作成します。 コレクションに直接バインドする場合、WPF はその既定のビューにバインドします。 この既定のビューは、同じコレクションへのすべてのバインディングで共有されます。そのため、1 つのバインドされたコントロールまたはコードによって既定のビューに加えられた変更 (後で説明する、現在の項目のポインターの並べ替えや変更など) は、同じコレクションへの他のすべてのバインディングにも反映されます。

既定のビューを取得するには、GetDefaultView メソッドを使用します。 例については、「方法 : データ コレクションの既定のビューを取得する」を参照してください。

ADO.NET DataTables を持つコレクション ビュー

パフォーマンスを向上させるため、ADO.NET DataTable オブジェクトまたは DataView オブジェクトのコレクション ビューは、並べ替えとフィルター処理を DataView に委任します。 これにより、データ ソースのすべてのコレクション ビューが、並べ替えとフィルター処理を共有するようになります。 各コレクション ビューが個別に並べ替えとフィルター処理を行うことができるようにするには、各コレクション ビューをそれ自体の DataView オブジェクトで初期化します。

並べ替え

既に説明したように、ビューでは、コレクションに並べ替え順序を適用できます。 基になるコレクションに並べ替え順序が存在している場合、関連する固有の順序がデータに適用されたり適用されなかったりすることがあります。 コレクション上のビューを使用すると、指定した比較条件に基づいて、順序を強制したり既定の順序を変更したりできます。 これはクライアント ベースのデータ ビューであるため、ユーザーが対応する列の値に応じてテーブル データの列を並べ替える必要がある場合によく使用されます。 ビューを使用すると、こうしたユーザーによる並べ替えを適用できます。この場合も基になるコレクションを変更する必要はありません。また、コレクションのコンテンツに対してクエリを再度実行する必要さえありません。 例については、「方法 : ヘッダーがクリックされたときに GridView 列を並べ替える」を参照してください。

次の例は、「データ バインディングとは」セクションで示したアプリケーション UI である [Sort by category and date] CheckBox の並べ替えロジックを示しています。

private void AddSorting(object sender, RoutedEventArgs args)
{
    // This sorts the items first by Category and within each Category,
    // by StartDate. Notice that because Category is an enumeration,
    // the order of the items is the same as in the enumeration declaration
    listingDataView.SortDescriptions.Add(
        new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(
        new SortDescription("StartDate", ListSortDirection.Ascending));
}

フィルター処理

ビューでは、コレクションにフィルターを適用することもできます。 つまり、この特定のビューは、コレクションに項目が存在する場合でも、コレクション全体の特定のサブセットのみを表示するためのものです。 データの条件に基づいてフィルター処理することができます。 たとえば、「データ バインディングとは」セクションで示したアプリケーションの [Show only bargains] CheckBox には、25 ドル以上の品目をフィルターで除外するロジックが含まれています。 この CheckBox をオンにすると、次のコードが実行され、ShowOnlyBargainsFilterFilter イベント ハンドラーとして設定されます。

listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);

ShowOnlyBargainsFilter イベント ハンドラーの実装例を次に示します。

private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
    AuctionItem product = e.Item as AuctionItem;
    if (product != null)
    {
        // Filter out products with price 25 or above
        if (product.CurrentPrice < 25)
        {
            e.Accepted = true;
        }
        else
        {
            e.Accepted = false;
        }
    }
}

CollectionViewSource の代わりに CollectionView クラスのいずれかを直接使用している場合は、Filter プロパティを使用してコールバックを指定します。 例については、「方法 : ビュー内のデータをフィルター処理する」を参照してください。

グループ化

IEnumerable コレクションを表示する内部クラスを除いて、すべてのコレクション ビューがグループ化の機能をサポートします。このため、ユーザーは、コレクション ビュー内でコレクションを論理グループに分割できます。 グループには、ユーザーがグループの一覧を提供する明示的なグループと、データに基づいて動的にグループが生成される暗黙的なグループがあります。

次の例は、[Group by category] CheckBox のロジックを示しています。

// This groups the items in the view by the property "Category"
PropertyGroupDescription groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);

他のグループ化の例については、「方法 : GridView を実装する ListView の項目をグループ化する」を参照してください。

現在の項目のポインター

ビューでは、現在の項目の概念もサポートされます。 コレクション ビュー内のオブジェクト間を移動できます。 オブジェクト間を移動するときは、コレクション内の特定の場所に存在するオブジェクトを取得できるようにする項目ポインターを移動しています。 例については、「方法 : データ CollectionView のオブジェクト間を移動する」を参照してください。

WPF がコレクションにバインドするときは必ずビュー (ユーザーが指定したビュー、またはコレクションの既定のビュー) を使用するため、コレクションへのすべてのバインディングには現在の項目のポインターがあります。 ビューにバインドするたきには、Path 値のスラッシュ ("/") 文字がそのビューの現在の項目を指定します。 次の例では、データ コンテキストがコレクション ビューになります。 最初の行はコレクションにバインドします。 2 番目の行はコレクション内の現在の項目にバインドします。 3 行目は、コレクション内の現在の項目の Description プロパティにバインドします。

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" /> 

スラッシュとプロパティ構文をスタックして、コレクション階層を走査することもできます。 次の例では、ソース コレクションの現在の項目のプロパティである、Offices という名前の、コレクションの現在の項目にバインドします。

<Button Content="{Binding /Offices/}" />

現在の項目のポインターは、コレクションに適用される並べ替えまたはフィルター処理によって影響を受けることがあります。 並べ替えでは、選択した最後の項目に現在の項目のポインターが維持されますが、コレクション ビューはその項目を中心にして再構築されます (リストの先頭にあった、選択された項目が、リストの中央部に移動することが考えられます)。 選択されていた項目がフィルター処理後にビュー内に残っている場合、その項目は保持されます。 それ以外の場合、現在の項目のポインターは、フィルター処理されたコレクション ビューの最初の項目に設定されます。

マスター詳細シナリオ

現在の項目の概念は、コレクション内の項目間の移動だけでなく、バインディングのマスター詳細シナリオでも役立ちます。 「データ バインディングとは」セクションのアプリケーション UI をもう一度考えてみましょう。 このアプリケーションでは、ListBox 内の選択内容によって ContentControl に表示されるコンテンツが決定されます。 つまり、ListBox の項目を選択すると、その項目の詳細が ContentControl に表示されます。

複数のコントロールを同じビューにバインドするだけで、マスター詳細シナリオを実装することができます。 次に示すのはデータ バインディングのデモからの例で、ListBox のマークアップおよび ContentControl です。これらは「データ バインディングとは」セクションのアプリケーション UI で参照しました。

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
    ItemsSource="{Binding Source={StaticResource listingDataView}}">


...


</ListBox>


...


<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3" 
        Content="{Binding Source={StaticResource listingDataView}}" 
        ContentTemplate="{StaticResource detailsProductListingTemplate}" 
        Margin="9,0,0,0"/>

両方のコントロールが同じソース (listingDataView 静的リソース) にバインドされていることに注意してください (「ビューを作成する方法」セクションにあるこのリソースの定義を参照)。 これが機能するのは、シングルトン オブジェクト (この場合は ContentControl) をコレクション ビューにバインドした場合、そのシングルトン オブジェクトが自動的にビューの CurrentItem にバインドされるためです。 CollectionViewSource オブジェクトによって現在の項目と選択した項目が自動的に同期されることに注意してください。 この例とは異なり、リスト コントロールが CollectionViewSource オブジェクトにバインドされない場合は、IsSynchronizedWithCurrentItem プロパティを true に設定して、現在の項目と選択した項目が同期されるようにする必要があります。

その他の例については、「方法 : コレクションにバインドして選択に基づく情報を表示する」および「方法 : 階層データでマスター詳細パターンを使用する」を参照してください。

上記の例ではテンプレートが使用されています。 実際、テンプレート (ContentControl によって明示的に使用されるテンプレートと ListBox によって暗黙的に使用されるテンプレート) を使用しない場合、データは意図したとおりの形式で表示されません。 次のセクションでは、データ テンプレートについて説明します。

データ テンプレート

データ テンプレートを使用しない場合、「データ バインディングとは」セクションのアプリケーション UI は、次のようになります。

データ テンプレートのないデータ バインディング デモ

前のセクションの例で示したように、ListBox コントロールおよび ContentControl の両方が、AuctionItems のコレクション オブジェクト全体 (具体的には、コレクション オブジェクトのビュー) にバインドされます。 データ コレクションの表示方法を特に指定していない場合は、基になるコレクション内の各オブジェクトの文字列表現が ListBox に表示され、バインド先のオブジェクトの文字列形式が ContentControl に表示されます。

この問題を解決するには、アプリケーションで DataTemplate を定義します。 前のセクションの例で示したように、ContentControldetailsProductListingTemplate DataTemplate を明示的に使用します。 ListBox コントロールは、コレクション内の AuctionItem オブジェクトを表示する際に、次の DataTemplate を暗黙的に使用します。

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="86"/>
            <ColumnDefinition Width="*"/>
          </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2" 
                Text="{Binding Path=Description}" 
                Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType" 
                    Text="{Binding Path=CurrentPrice}" 
                    Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
          <DataTrigger.Setters>
            <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
          </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

これら 2 つの DataTemplate を使用すると、「データ バインディングとは」セクションで示した UI が表示されます。 この UI のスクリーンショットからわかるように、DataTemplate を使用すると、コントロール内にデータを配置できるだけでなく、優れた視覚効果をデータに対して定義することができます。 たとえば、上の DataTemplate では、DataTrigger を使用しています。これにより、HighLight の値が SpecialFeatures に設定されている AuctionItem が、星の付いたオレンジ色の境界線で表示されます。

データ テンプレートの詳細については、「データ テンプレートの概要」を参照してください。

データの検証

ここでは、次の項目について説明します。

  • 検証規則とバインディングの関連付け
  • 視覚的フィードバックの提供
  • 検証プロセス

ユーザー入力を受け取るほとんどのアプリケーションでは、ユーザーが必要な情報を入力したことを確認するための検証ロジックが必要になります。 型、範囲、形式、またはその他のアプリケーション固有の要件に基づいて、検証チェックを実行することができます。 ここでは、WPF におけるデータ検証のしくみについて説明します。

検証規則とバインディングの関連付け

WPF データ バインディング モデルでは、ValidationRulesBinding オブジェクトに関連付けることができます。 たとえば、次の例では TextBox を StartPrice という名前のプロパティにバインドし、ExceptionValidationRule オブジェクトを Binding.ValidationRules プロパティにバインドします。

<TextBox Name="StartPriceEntryForm" Grid.Row="2" Grid.Column="1"
    Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
  <TextBox.Text>
    <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <ExceptionValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

ValidationRule オブジェクトは、プロパティ値が有効であるかどうかを確認します。 WPF には、次の 2 種類の組み込み ValidationRule オブジェクトがあります。

  • ExceptionValidationRule では、バインディング ソース プロパティの更新中にスローされる例外をチェックします。 前の例では、StartPrice の型は整数です。 ユーザーが整数に変換できない値を入力すると、例外がスローされ、バインディングが無効としてマークされます。 また、ExceptionValidationRule を明示的に設定するには、Binding オブジェクトまたは MultiBinding オブジェクトで、ValidatesOnExceptions プロパティを true に設定する構文も使用できます。

  • DataErrorValidationRule オブジェクトは、IDataErrorInfo インターフェイスを実装するオブジェクトで発生するエラーをチェックします。 この検証規則の使用例については、「DataErrorValidationRule」を参照してください。 また、DataErrorValidationRule を明示的に設定するには、Binding オブジェクトまたは MultiBinding オブジェクトで、ValidatesOnDataErrors プロパティを true に設定する構文も使用できます。

ValidationRule クラスを継承し、Validate メソッドを実装することにより、独自の検証規則を作成することもできます。 次の例は、「データ バインディングとは」セクションで示した [Add Product Listing] の [Start Date] TextBox で使用される規則を示しています。

class FutureDateRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        DateTime date;
        try
        {
            date = DateTime.Parse(value.ToString());
        }
        catch (FormatException)
        {
            return new ValidationResult(false, "Value is not a valid date.");
        }
        if (DateTime.Now.Date > date)
        {
            return new ValidationResult(false, "Please enter a date in the future.");
        }
        else
        {
            return ValidationResult.ValidResult;
        }
    }
}

次の例に示すように、StartDateEntryForm TextBox では、この FutureDateRule が使用されます。

<TextBox Name="StartDateEntryForm" Grid.Row="3" Grid.Column="1" 
    Validation.ErrorTemplate="{StaticResource validationTemplate}" 
    Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
            Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

UpdateSourceTrigger の値が PropertyChanged であるため、バインディング エンジンは、キーストロークが発生するたびにソース値を更新します。つまり、ValidationRules コレクション内のすべての規則もまた、キーストロークが発生するたびにバインディング エンジンによってチェックされることになります。 詳細については、「検証プロセス」セクションで説明します。

視覚的フィードバックの提供

ユーザーが無効な値を入力した場合には、アプリケーションの UI 上でエラーに関するフィードバックを提供する必要があります。 このようなフィードバックを提供する方法として、Validation.ErrorTemplate 添付プロパティをカスタム ControlTemplate に設定する方法があります。 前のサブセクションで示したように、StartDateEntryForm TextBox では、validationTemplate という名前の ErrorTemplate が使用されます。 validationTemplate の定義を次の例に示します。

<ControlTemplate x:Key="validationTemplate">
  <DockPanel>
    <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
    <AdornedElementPlaceholder/>
  </DockPanel>
</ControlTemplate>

AdornedElementPlaceholder 要素は、装飾されているコントロールの位置を指定します。

また、ToolTip を使用してエラー メッセージを表示することもできます。 StartDateEntryForm および StartPriceEntryForm の両 TextBox では、textStyleTextBox スタイルが使用されています。これにより、エラー メッセージを表示する ToolTip が作成されます。 textStyleTextBox の定義を次の例に示します。 バインドされている要素のプロパティで 1 つ以上のバインディングにエラーが発生すると、Validation.HasError 添付プロパティが true になります。

<Style x:Key="textStyleTextBox" TargetType="TextBox">
  <Setter Property="Foreground" Value="#333333" />
  <Setter Property="MaxLength" Value="40" />
  <Setter Property="Width" Value="392" />
  <Style.Triggers>
    <Trigger Property="Validation.HasError" Value="true">
      <Setter Property="ToolTip"
        Value="{Binding RelativeSource={RelativeSource Self},
                        Path=(Validation.Errors)[0].ErrorContent}"/>
    </Trigger>
  </Style.Triggers>
</Style>

カスタム ErrorTemplateToolTip を使用している場合、検証エラーが発生すると、StartDateEntryForm TextBox は次のようになります。

データ バインディング検証エラー

Binding に検証規則が関連付けられていても、バインドされているコントロールに対して ErrorTemplate を指定していない場合は、検証エラーが発生したときのユーザーへの通知に既定の ErrorTemplate が使用されます。 既定の ErrorTemplate は、装飾層に赤い境界線を定義するコントロール テンプレートです。 既定の ErrorTemplateToolTip を使用している場合、検証エラーが発生すると、StartPriceEntryForm TextBox の UI は次のようになります。

データ バインディング検証エラー

ダイアログ ボックス内のすべてのコントロールを検証するロジックを提供する方法の例については、「ダイアログ ボックスの概要」の「カスタム ダイアログ ボックス」セクションを参照してください。

検証プロセス

検証は、通常、ターゲットの値がバインディング ソース プロパティに転送されると発生します。 検証は、TwoWay バインディングと OneWayToSource バインディングで行われます。 「ソースの更新の要因」セクションで説明したように、ソースの更新の要因は、UpdateSourceTrigger プロパティの値によって異なります。

ここでは、検証プロセスについて説明します。 このプロセスの最中に検証エラーなどのエラーが発生すると、プロセスが中止されることに注意してください。

  1. バインディング エンジンは、該当する Binding について ValidationStepRawProposedValue に設定されているカスタム ValidationRule オブジェクトが定義されているかどうかをチェックします。このとき、どれかでエラーが発生するか、すべてが合格するまで、各 ValidationRuleValidate メソッドを呼び出します。

  2. 次に、バインディング エンジンはコンバーターを呼び出します (存在する場合)。

  3. コンバーターが正常終了した場合、バインディング エンジンは、該当する Binding について ValidationStepConvertedProposedValue に設定されているカスタム ValidationRule オブジェクトが定義されているかどうかをチェックします。このとき、いずれかでエラーが発生するか、すべてが合格するまで、ValidationStepConvertedProposedValue に設定されている各 ValidationRuleValidate メソッドを呼び出します。

  4. バインディング エンジンがソース プロパティを設定します。

  5. バインディング エンジンは、該当する Binding について ValidationStepUpdatedValue に設定されているカスタム ValidationRule オブジェクトが定義されているかどうかをチェックします。このとき、いずれかでエラーが発生するか、すべてが合格するまで、ValidationStepUpdatedValue に設定されている各 ValidationRuleValidate メソッドを呼び出します。 DataErrorValidationRule がバインディングに関連付けられており、その ValidationStep が既定の UpdatedValue に設定されている場合、この時点で DataErrorValidationRule がチェックされます。 また、この時点で、ValidatesOnDataErrors が true に設定されているバインディングもチェックされます。

  6. バインディング エンジンは、該当する Binding について ValidationStepCommittedValue に設定されているカスタム ValidationRule オブジェクトが定義されているかどうかをチェックします。このとき、いずれかでエラーが発生するか、すべてが合格するまで、ValidationStepCommittedValue に設定されている各 ValidationRuleValidate メソッドを呼び出します。

このプロセスの最中に ValidationRule が合格しなかった場合、バインディング エンジンは ValidationError オブジェクトを作成し、それをバインドされている要素の Validation.Errors コレクションに追加します。 バインディング エンジンは、各手順で ValidationRule オブジェクトを実行する前に、バインドされた要素の Validation.Errors 添付プロパティにその手順で以前追加されたすべての ValidationError を削除します。 たとえば、ValidationStepUpdatedValue に設定された ValidationRule が不合格になった場合、次回このプロセスが発生したとき、バインディング エンジンはそのような ValidationError を、ValidationStepUpdatedValue に設定されている ValidationRule を呼び出す直前に削除します。

Validation.Errors が空でない場合、その要素の Validation.HasError 添付プロパティは true に設定されます。 また、BindingNotifyOnValidationError プロパティが true に設定されている場合、バインディング エンジンはその要素の Validation.Error  添付イベントを発生させます。

また、どちらの方向 (ターゲットからソース、またはソースからターゲット) でも、有効な値を転送すると Validation.Errors 添付プロパティが消去されることに注意してください。

バインディングに ExceptionValidationRule が関連付けられているか、ValidatesOnExceptions プロパティが true に設定されており、バインディング エンジンがソースを設定したときに例外がスローされた場合、バインディング エンジンは UpdateSourceExceptionFilter が存在するかどうかをチェックします。 UpdateSourceExceptionFilter コールバックを使用して、例外を処理するためのカスタム ハンドラーを提供することもできます。 UpdateSourceExceptionFilterBinding で指定されていない場合、バインディング エンジンは例外を使用して ValidationError を作成し、それをバインドされている要素の Validation.Errors コレクションに追加します。

デバッグ機構

バインディング関連のオブジェクトに対して PresentationTraceSources.TraceLevel 添付プロパティを設定すると、特定のバインディングのステータス情報を取得できます。

参照

処理手順

方法 : LINQ クエリの結果にバインドする

方法 : ADO.NET データ ソースにバインドする

参照

DataErrorValidationRule

概念

WPF Version 4 の新機能

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

その他の技術情報

データ バインディングのデモ

データ バインディングに関する「方法」トピック