データ バインディングは、Silverlight ベースのアプリケーションがデータを表示し、データと対話するための簡単な方法を提供します。データの表示方法は、データの管理とは分離されています。UI とデータ オブジェクトの間の接続 (バインディング) を確立すると、データが UI とデータ オブジェクトの間を移動できるようになります。バインディングが確立され、データが変更されると、データにバインドされている UI 要素にその変更が自動的に反映されます。同様に、ユーザーが UI 要素で行った変更を、データ オブジェクトに反映できます。たとえば、ユーザーが TextBox の値を編集した場合、基になるデータの値が自動的に更新されてその変更が反映されます。
一般的なバインディング シナリオとして、ヘッドラインの一覧への ListBox のバインド、顧客データ オブジェクトへの入力フォームの TextBox のバインド、現在のユーザーの写真への Image のバインドなどがあります。
このトピックは、次のセクションで構成されています。
バインディングを確立するには、ソースとターゲットを必ず指定する必要があります。次の図に、バインディングの基本概念を示します。
.png)
バインディング エンジンは、次の要素に関する情報を Binding オブジェクトから取得します。
データを表示し、場合によってはユーザーによるデータの変更を許可するターゲット UI プロパティ。ターゲットとしては、FrameworkElement の任意の DependencyProperty を使用できます。
ソースとターゲットの間を移動するデータを含むソース オブジェクト。ソースは、ターゲット要素自体または他の UI 要素を含めた、任意の CLR オブジェクトです。ターゲットがデータ テンプレートに含まれる場合は、テンプレートが適用される UI 要素をソースとして使用できます。
データ フローの方向。方向は、Binding オブジェクトの Mode プロパティを設定して指定します。
渡されるデータに適用される、省略可能な値コンバーター。値コンバーターは、IValueConverter を実装するクラスです。
たとえば、TextBox の Foreground プロパティを SolidColorBrush にバインドして、データに基づいてテキストの色を変更できます。このシナリオでは、Foreground プロパティがバインディングのターゲット、SolidColorBrush オブジェクトがソースです。
次の例は、TextBox の Foreground 色を SolidColorBrush にバインドする方法をコードおよび XAML で示しています。バインディング ソースは、このトピックで後述する MyColors クラスのプロパティの 1 つです。
<TextBox x:Name="MyTextBox" Text="Text" Foreground="{Binding Brush1, Mode=OneWay}"/>
' Create an instance of the MyColors class
' that implements INotifyPropertyChanged.
Dim textcolor As New MyColors()
' Brush1 is set to be a SolidColorBrush with the value Red.
textcolor.Brush1 = New SolidColorBrush(Colors.Red)
' Set the DataContext of the TextBox MyTextBox.
MyTextBox.DataContext = textcolor
メモ : |
|---|
この例では、バインディングの作成に XAML 属性構文を使用しています。XAML でバインディングを作成するには、オブジェクト要素構文を使用することもできます。詳細については、「XAML の概要」を参照してください。 |
バインディングは、XAML で {Binding ...} 構文を使用して作成します。TextBox の DataContext プロパティを設定して、ソースをコードに設定します。
データ コンテキストは継承されます。データ コンテキストを親要素に設定すると、その子もすべて同じデータ コンテキストを使用します。バインディング オブジェクトの Source プロパティを設定するか、またはそのすべての子に適用される DataContext を設定すると、子要素はこの動作をオーバーライドできます。
データ コンテキストの設定は、同じソースを使用する複数のバインディングが必要な場合に便利です。単一バインディングのソースを設定するには、Binding オブジェクトの Source プロパティを設定します。詳細については、「方法 : バインディングを作成する」を参照してください。
ElementName プロパティまたは RelativeSource プロパティを使用してバインディング ソースを指定することもできます。ElementName プロパティは、アプリケーション内の他の要素にバインドする場合 (スライダーを使用してボタンの幅を調整する場合など) に役立ちます。RelativeSource プロパティは、ControlTemplate または Style でバインディングを指定する場合に役立ちます。詳細については、「Binding のマークアップ拡張機能」および「RelativeSource マークアップ拡張機能」を参照してください。
つまり、前の例のバインディング エンジンでは、TextBox の Foreground プロパティを TextColor オブジェクトの brush1 プロパティに関連付けて、バインディング (既定では OneWay) を作成しています。
各バインディングには、データ フローの方法とタイミングを決定する Mode プロパティがあります。Silverlight では、次の 3 種類のバインディングを行うことができます。
OneTime バインディングは、バインディングの作成時にソース データでターゲットを更新します。
OneWay バインディングは、バインディングの作成時とデータの変更のたびにソース データでターゲットを更新します。これは、既定のモードです。
TwoWay バインディングは、ターゲットまたはソースが変更されたときにターゲットとソースの両方を更新します。または、ソースの自動更新を無効にして、選択したときにだけソースを更新することもできます。
ターゲットの自動更新を実行するには、次のセクションで説明するように、ソース オブジェクトで INotifyPropertyChanged インターフェイスを実装する必要があります。
ソース オブジェクトに加えた変更をターゲットに反映させるには、ソースで INotifyPropertyChanged インターフェイスを実装する必要があります。INotifyPropertyChanged には PropertyChanged イベントがあり、ソースが変更されたことをバインディング エンジンに通知するので、バインディング エンジンはターゲット値を更新できます。
次の例では、MyColors クラスが OneWay バインディングのための INotifyPropertyChanged インターフェイスを実装します。
' Create a class that implements INotifyPropertyChanged.
Public Class MyColors
Implements INotifyPropertyChanged
Private _Brush1 As SolidColorBrush
' Declare the PropertyChanged event.
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
' Create the property that will be the source of the binding.
Public Property Brush1() As SolidColorBrush
Get
Return _Brush1
End Get
Set(ByVal value As SolidColorBrush)
_Brush1 = value
' Call NotifyPropertyChanged when the source property
' is updated.
NotifyPropertyChanged("Brush1")
End Set
End Property
' NotifyPropertyChanged will raise the PropertyChanged event,
' passing
the source property that is being updated.
Public Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, _
New PropertyChangedEventArgs(propertyName))
End Sub
End Class
ItemsControl にバインドされているコレクションの変更通知を取得するには、INotifyPropertyChanged に加えて INotifyCollectionChanged を実装します。INotifyCollectionChanged を実装した場合、コレクションに加えられた変更 (たとえばオブジェクトの追加、削除) は、ターゲットに反映されます。コレクション内のオブジェクトのプロパティ変更通知を取得するには、オブジェクトで INotifyPropertyChanged を実装する必要があります。
独自のコレクション クラスを実装する前に、INotifyCollectionChanged および INotifyPropertyChanged の組み込み実装を含む ObservableCollection<(Of <(T>)>) クラスの使用を検討してください。
TwoWay バインディングでは、TextBox の Text プロパティへのバインディングを除き、ターゲットが変更されるとソースが自動的に更新されます。この場合は、TextBox がフォーカスを失ったときに更新が行われます。
ソースの自動更新を無効にして、選択したときにソースを更新することができます。この方法は、たとえば、バインドされたデータ ソースを更新する前に複数のコントロールからのユーザー入力を検証する場合などに使用できます。
ソースの自動更新を無効にするには、UpdateSourceTrigger プロパティを Explicit に設定します。この設定は、同じ Binding オブジェクトを使用する (継承されたデータ コンテキストを使用している場合など) すべてのバインディングに適用されます。ただし、各バインディングのソースを個別に更新する必要があります。バインディングを更新するには、最初にターゲット要素の FrameworkElement..::.GetBindingExpression メソッドを呼び出して、ターゲットの DependencyProperty を渡します。次に、その戻り値を使用して、BindingExpression..::.UpdateSource メソッドを呼び出します。この処理のコード例を次に示します。
<TextBox x:Name="textBox1" Text="{Binding Test, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />
<Button Content="Update" Click="Button_Click" />
Public Class TestData
Private testValue As String
Public Property Test() As String
Get
Return testValue
End Get
Set(ByVal value As String)
testValue = value
End Set
End Property
End Class
Private data As TestData
Public Sub New()
InitializeComponent()
data = New TestData
With data
.Test = "one"
End With
textBox1.DataContext = data
End Sub
Private Sub Button_Click(ByVal sender As Object, _
ByVal e As RoutedEventArgs)
Dim expression As BindingExpression = _
textBox1.GetBindingExpression(TextBox.TextProperty)
MessageBox.Show("Before UpdateSource, Test = " & data.Test)
expression.UpdateSource()
MessageBox.Show("After UpdateSource, Test = " & data.Test)
End Sub
バインディング ソース オブジェクトは、プロパティにデータが含まれる単一のオブジェクト、またはオブジェクトのコレクションとして処理できます。たとえば、毎月のクレジット カード請求書などの項目一覧を表示する場合が考えられます。これを行うには、ItemsControl を使用し、DataTemplate に対する反復処理を行ってコレクション内の各項目を表示します。データ テンプレートの詳細については、「方法 : データ テンプレートを使用してデータ表示をカスタマイズする」を参照してください。
<Grid.Resources>
<DataTemplate x:Name="dataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Month, Converter={StaticResource Converter1}}"/>
<TextBlock Grid.Column="1" Text="{Binding Total}"/>
</Grid>
</DataTemplate>
</Grid.Resources>
<ItemsControl x:Name="IC1" ItemsSource="{Binding}"
ItemTemplate="{StaticResource dataTemplate}"/>
IEnumerable を実装するすべてのコレクションを列挙できます。コレクションが変更されたときにターゲットが ItemsSource を更新するように設定するには、INotifyCollectionChanged を実装します。変更通知の詳細については、このトピックの前の「変更通知」を参照してください。
データ コレクションへのバインディングの作成については、「チュートリアル : コレクションへのバインドとマスター/詳細ビューの作成」を参照してください。
コントロールをバインドできるようにオブジェクトのジェネリック リストをカプセル化する方法については、List<(Of <(T>)>) および Dictionary<(Of <(TKey, TValue>)>) の最初のコード例を参照してください。
Silverlight はターゲットからソースへの更新用の TwoWay バインディングで基本的なデータ検証をサポートしています。Silverlight では次の場合に検証エラーが報告されます。
バインディング エンジンの型コンバーターからスローされた例外。
バインディング オブジェクトの set アクセサーからスローされた例外。
データ オブジェクトまたはメンバーに適用される検証属性からスローされた例外。
Silverlight では、バインディング オブジェクトの ValidatesOnExceptions プロパティを true に設定すると、検証エラーの視覚的フィードバックが表示されます。次の図に示すように、視覚的フィードバックには、エラーを含むコントロールが示され、近くにエラー メッセージが表示されます。
.png)
コントロールの視覚的フィードバックは、その既定の ControlTemplate を変更または置換することでカスタマイズできます。詳細については、「コントロールのカスタマイズ」を参照してください。
また、検証エラーが発生したという通知を受け取るには、バインディング オブジェクトの NotifyOnValidationError プロパティを true に設定する必要があります。ValidatesOnExceptions を true に設定すると、例外が発生したときに検証エラーを作成するようにバインディング エンジンに指示します。NotifyOnValidationError を true に設定すると、検証エラーが発生して解決したときに BindingValidationError イベントを発生するようにバインディング エンジンに指示します。たとえば、エラー イベントを処理して、エラーをログに記録したり、視覚的フィードバックを追加したりできます。
BindingValidationError イベントを処理するには、ターゲット オブジェクトまたはその親にイベント ハンドラーを作成します。BindingValidationError イベントはルーティング イベントなので、イベントを生成した要素で処理しないと、そのイベントは処理されるまで伝達され続けます。ルーティング イベントの詳細については、「Silverlight のイベントの概要」を参照してください。
次の例で、カスタム バインディングを検証する方法を示します。
バインディングは XAML で作成します。
<StackPanel BindingValidationError="StackPanel_BindingValidationError" >
<StackPanel.Resources>
<my:Bills x:Name="MyBills"/>
</StackPanel.Resources>
<TextBox x:Name="MyTextBox" Width="50" Margin="10">
<TextBox.Text>
<Binding Mode="TwoWay" Source="{StaticResource MyBills}"
Path="Amount" NotifyOnValidationError="true"
ValidatesOnExceptions="true"/>
</TextBox.Text>
</TextBox>
<Button Height="50" Width="150" Content="Click To Update Source"/>
</StackPanel>
値が負の場合は、set アクセサーでソース オブジェクトが例外をスローします。
Public Class Bills
Private _Amount As Double
Public Property Amount() As Double
Get
Return _Amount
End Get
Set(ByVal value As Double)
If value < 0 Then
Throw New Exception("Amount must be greater than zero.")
End If
_Amount = value
End Set
End Property
End Class
StackPanel は BindingValidationError イベントのハンドラーを実装します。
Private Sub StackPanel_BindingValidationError(ByVal sender As Object, _
ByVal e As ValidationErrorEventArgs)
If e.Action = ValidationErrorEventAction.Added Then
MyTextBox.Background = New SolidColorBrush(Colors.Red)
ElseIf e.Action = ValidationErrorEventAction.Removed Then
MyTextBox.Background = New SolidColorBrush(Colors.White)
End If
End Sub
このサンプルを実行する
型コンバーターで発生したエラーを取得するには、サンプルを起動した後、数字ではなく文字を入力します。ソース オブジェクトの set アクセサーからのエラーを取得するには、負の数を入力します。検証エラーを解決するには、正の数を入力します。TextBox ターゲットからソースへの更新は、TextBox がフォーカスを失った場合にのみ発生します。フォーカスを変更するためのボタンが用意されています。必要であれば、「データ ソースの更新」で説明したように、ボタンのクリックに応答してソースを手動で更新できます。
検証属性を適用してプロパティまたはオブジェクトの許可値を指定する方法については、「データ注釈を使用したデータ クラスのカスタマイズ」を参照してください。詳細な検証報告を行う方法については、ValidationSummary クラスの概要を参照してください。
データを、格納されている形式とは異なる形式で表示することが必要になる場合もあります。次にいくつかの例を示します。
RGBA 値として格納した色を文字列名で表示する場合。
浮動小数点値として格納した数を通貨値で表示する場合。
DateTime として格納した日付をカレンダーに表示する場合。
どのバインディングにもコンバーターを設定できます。コンバーターは、クラスを作成して IValueConverter インターフェイスを実装することにより、シナリオごとにカスタマイズします。IValueConverter を実装する方法を次の例に示します。
' Custom class implements the IValueConverter interface.
Public Class DateToStringConverter
Implements IValueConverter
' Define the Convert method to change a DateTime object to
' a month string.
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
' value is the data from the source object.
Dim thisdate As DateTime = CType(value, DateTime)
Dim monthnum As Integer = thisdate.Month
Dim month As String
Select Case (monthnum)
Case 1
month = "January"
Case 2
month = "February"
Case Else
month = "Month not found"
End Select
' Return the value to pass to the target.
Return month
End Function
' ConvertBack is not implemented for a OneWay binding.
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
Throw New NotImplementedException
End Function
End Class
バインディングの Converter パラメーターを定義した場合、Convert メソッドと ConvertBack メソッドがバインディング エンジンによって呼び出されます。ソースからデータが渡されると、バインディング エンジンは、Convert を呼び出し、返すデータをターゲットに渡します。ターゲットからデータが渡されると、バインディング エンジンは、ConvertBack を呼び出し、返すデータをソースに渡します。Converter パラメーターを設定する方法を次の例に示します。
<UserControl.Resources>
<local:DateToStringConverter x:Key="Converter1"/>
</UserControl.Resources>
...
<TextBlock Grid.Column="0"
Text="{Binding Month, Converter={StaticResource Converter1}}"/>
コンバーターには省略可能なパラメーターもあります。変換で使用するカルチャを指定できる ConverterCulture、および変換ロジックに渡すパラメーターを指定できる ConverterParameter です。これらのパラメーターの使用例については、「IValueConverter」を参照してください。
変換でエラーが発生した場合は、例外をスローしないでください。代わりに DependencyProperty..::.UnsetValue を返します。これにより、データ転送が中止されます。
その他の技術情報