XAML/Silverlightでデータ バインディング:連載:次世代技術につながるSilverlight入門(2/3 ページ)
データの種類に応じてUI表示をカスタマイズしたい?! XAMLなら簡単。データ・バインディングとデータ・テンプレートを解説。
データ・バインディング
データ・バインディング(data binding: データの結合、束縛)は、その名のとおり、あるデータと別のあるデータを結び付ける機能だ。ここでいう「結び付ける」というのは、「片方の値の変化と連動してもう一方の値も変化させる」という意味である。図 2に示すように、外部から与えられたデータをUIに反映させたり、UI要素間のプロパティ値を連動させたりといったことができる。
●Bindingオブジェクト
図 2にもすでに出てきているが、データ・バインディングは、リスト 4に示すようにBindingマークアップ拡張を使うか、リスト 5に示すように<Binding>要素を使って設定する。
<Grid Width="200" Grid.ColumnSpan="2" Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Slider x:Name="slider1" Value="3.2" />
<TextBox Text="{Binding ElementName=slider1, Path=Value}" Grid.Row="1" />
</Grid>
リスト 4: Bindingマークアップ拡張を使ったデータ・バインディングの例(XAML)
<TextBox Grid.Row="1">
<TextBox.Text>
<Binding ElementName="slider1" Path="Value" />
</TextBox.Text>
</TextBox>
リスト 5: 要素構文を使ったデータ・バインディングの例(XAML)
いずれの例も、スライダの値をテキストボックス内にテキストとして表示する。スライダのつまみを動かすと、即座にテキストボックス側の値も変化する。
マークアップ拡張と要素構文のどちらを使うにしても、特殊な処理が行われていて、単純なプロパティ値代入にはならない。プログラム・コードで書くならリスト 6に示すような、Bindingオブジェクトを介した処理が行われる。
var binding = new System.Windows.Data.Binding
{
ElementName = "slider1",
Path = new System.Windows.PropertyPath("Value"),
};
text1.SetBinding(TextBox.TextProperty, binding);
Dim binding As New System.Windows.Data.Binding With
{
.ElementName = "slider1",
.Path = New System.Windows.PropertyPath("Value")
}
text1.SetBinding(TextBox.TextProperty, binding)
リスト 6: プログラム・コード中でBindingオブジェクトを使ってデータ・バインディングする例(上:C#、下:VB)
○ターゲットとソース
図 3に示すように、データ・バインディングには「向き」があり、データの提供元の側を「ソース(source)」、反映先の側を「ターゲット(target)」と呼ぶ。後述するように、Bindingマークアップ拡張に与える値次第で、ターゲット側からソース側への書き戻しを行うかどうかも設定できる。
ソース側は、任意のオブジェクトの任意のプロパティ(またはオブジェクト自身)を設定できる。一方で、ターゲット側は依存関係プロパティである必要がある。
【コラム】匿名型やdynamic型
Silverlightの場合、データ・バインディングのソースとして使えるのは、publicな型のpublicなプロパティのみとなる。このため、匿名型(=privateなクラスになる)をデータ・バインディングで利用できない。
また、dynamic型とExpnandoObjectクラス(System.Dynamic名前空間)などを使って動的に作成したオブジェクト(=正確にいうと、IDynamicMetaObjectProviderインターフェイス(System.Dynamic名前空間)を実装するオブジェクト)も、データ・バインディングのソースにできない。
ちなみに、WPFやWindowsストア・アプリのXAMLではこのような制限はかからない。
【コラム】実行時型情報
データ・バインディングでは、実行時型情報(=リフレクション)を調べて、動的な処理が行われている。
Silverlight 4以降では、ICustomTypeProviderインターフェイス(System.Reflection名前空間)を使うことで、本来の型情報ではなく実行時に生成したカスタム型情報に基づいたデータ・バインディングもできる。
ちなみに、ICustomTypeProviderインターフェイスはWPF 4.5でも利用可能だ。それ以前のWPFでは、同様の要件に対してICustomTypeDescriptorインターフェイス(System.ComponentModel名前空間)を使っていたが、このインターフェイスはType型とは別系統のTypeDescriptorというクラスを使ってカスタム型情報を表現していた(2系統の型情報の重複を避けるため、ICustomTypeProviderインターフェイスが新たに作られた)。
Windowsストア・アプリには同様の仕組みはない。
●ソース・オブジェクトの指定方法
Bindingオブジェクトは、バインディングのソースとなるオブジェクト(=ソース・オブジェクト)を指定するためのプロパティとして以下の3つを持っている。
- Sourceプロパティ: ソース・オブジェクト自体を渡す
- ElementNameプロパティ: XAMLコード中のほかのUI要素を参照する(ソースにしたいUI要素に対してx:Name属性で与えた名前を指定する)
- RelativeSourceプロパティ: ターゲットからの相対位置を指定して、その位置にあるUI要素を参照する
3つのうち2つ以上を同時に指定することはできない。
一方、3つとも省略した場合、後述するデータ・コンテキストが暗黙的にソース・オブジェクトとして使われる。
また、後述するPathプロパティを省略するとオブジェクト自身がデータ・バインディングのソースになり、Pathプロパティを与えるとそのオブジェクトのプロパティがソースになる。
○データ・コンテキスト
一番利用する機会が多いのは、前述の3つのプロパティすべてを省略するパターンになるだろう。この場合、「データ・コンテキスト(data context: データの文脈)」というものが使われる。
データ・コンテキストとはFrameworkElementクラス(System.Windows名前空間)のDataContextというプロパティに与えたオブジェクトのことで、ソースを明示しなかった場合、暗黙的にこの値がソース・オブジェクトとなる。
DataContextプロパティは親要素から値が引き継がれるため、最上位の親要素であるルート要素にだけ設定して使うことが多い。必要な場合だけ明示的にデータ・コンテキストを差し替える。
DataContextプロパティの利用例をリスト 7に示す。
<UserControl
x:Class="TemplateSample.Views.SilverlightControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:TemplateSample.DataModels">
<UserControl.DataContext>
<data:Triangle>
<data:Triangle.A><data:Point X="0" Y="0" /></data:Triangle.A>
<data:Triangle.B><data:Point X="3" Y="0" /></data:Triangle.B>
<data:Triangle.C><data:Point X="0" Y="4" /></data:Triangle.C>
</data:Triangle>
</UserControl.DataContext>
<Grid>
<StackPanel>
<StackPanel DataContext="{Binding A}" Orientation="Horizontal">
<TextBox Text="{Binding X}" />
<TextBox Text="{Binding Y}" />
</StackPanel>
<StackPanel DataContext="{Binding B}" Orientation="Horizontal">
<TextBox Text="{Binding X}" />
<TextBox Text="{Binding Y}" />
</StackPanel>
<StackPanel DataContext="{Binding C}" Orientation="Horizontal">
<TextBox Text="{Binding X}" />
<TextBox Text="{Binding Y}" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
リスト 7: DataContextの利用例(XAML)
この時、それぞれのUI要素におけるDataContextは図 4に示すようになっている。
○Sourceプロパティ
BindingオブジェクトのSourceプロパティを設定すると、ソース・オブジェクトを明示的に指定できる。ソース・オブジェクトを明示したい場合の最たる例は、リソース中のオブジェクトをソース・オブジェクトにする場合だろう。リスト 8に示すような使い方をする。
<UserControl
x:Class="TemplateSample.Views.SilverlightControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:TemplateSample.DataModels">
<UserControl.Resources>
<data:Triangle x:Key="data">
<data:Triangle.A><data:Point X="1" Y="2" /></data:Triangle.A>
</data:Triangle>
</UserControl.Resources>
<TextBox Text="{Binding Source={StaticResource data}, Path=A.X}" />
</UserControl>
リスト 8: Sourceプロパティを使ってリソースを参照する例(XAML)
○ElementNameプロパティ
BindingオブジェクトのElementNameプロパティを設定すると、XAMLコード中のほかのUI要素を参照する。例えば前述のリスト 4の例のように、スライダによってほかのUI要素中に表示されている値を変化させる場合などに利用できる。
○RelativeSourceプロパティ
BindingオブジェクトのRelativeSourceプロパティを設定すると、ターゲットからの相対位置を指定して、その位置にあるUI要素を参照する。
RelativeSourceプロパティには、RelativeSourceマークアップ拡張を与える。SilverlightのRelativeSourceマークアップ拡張では、以下のいずれかのモード指定(Modeプロパティに設定)ができる。
- Self: 自分自身
- TemplatedParent: (コントロール・テンプレート中でのみ)テンプレートの適用先のコントロール
- FindAncestor: 親要素をたどって、指定した条件を満たすUI要素を探す(ただし、Silverlight 5からの新機能)
●ソースのプロパティ・パス
BindingオブジェクトのPathプロパティで、オブジェクトのどのプロパティをソースにするかを指定する。Pathプロパティを省略した場合には、オブジェクト自身がソースになる。
○位置パラメータ
Bindingマークアップ拡張では、位置パラメータ(「{Binding Path=…… }」というようにプロパティ名を指定するのではなく、「{Binding …… }」というように直接、値を渡す方法)でもパスを指定できる。従って、表 1の左側と右側の列は同じ意味になる。
位置パラメータ | 名前付きパラメータ | |
---|---|---|
{Binding X} | {Binding Path=X} | |
{Binding X, ElementName=source} |
{Binding ElementName=source, Path=X} |
○プロパティ・パス構文
パスは、いろいろなプロパティ参照の仕方ができる構文をサポートしている。
- 単一のプロパティを参照するには、単純にプロパティ名を書く
- 「.」(ドット)でプロパティ名をつなぐことで、階層的に子オブジェクトのプロパティをたどれる
- 「[]」でインデクサを参照できる
- 「[0]」というように、整数インデックスを利用可能
- 「[key]」というように、文字列インデックスを利用可能(Silverlight 4以降)
- 1次元のインデクサのみ(「[0, 1]」というような多次元インデックス指定はできない)
- 「(<型名>.<プロパティ名>)」というように、「()」でくくって、型名を明示できる
いくつかの例を表 2に示す。この例では、データ・バインディングのソースとして、リスト 9に示すようなUI要素を使う。
<CheckBox x:Name="check">
<TextBlock x:Name="text" Text="sample text" ToolTipService.ToolTip="help text">
<TextBlock.RenderTransform>
<TransformGroup x:Name="transform">
<RotateTransform Angle="10" />
<ScaleTransform ScaleX="1.5" />
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
</CheckBox>
リスト 9: 例のソースとして使うUI要素(XAML)
例 | 説明 | |
---|---|---|
{Binding ElementName=text} | Pathプロパティの省略。<TextBox>要素自体がソースになる | |
{Binding Text, ElementName=text} | <TextBox>要素のTextプロパティがソースになる | |
{Binding ElementName=check, Path=Content.Text} | 階層的なプロパティ参照 | |
{Binding ElementName=check, Path=Content.(TextBlock.Text)} | 型の明示。前行と同じ意味。 スタイルやテンプレート中でデータ・バインディングを行う場合には型の明示が必要になる場合がある |
|
{Binding ElementName=text, Path=(ToolTipService.ToolTip)} | 添付プロパティを参照する場合には型の明示が必要になる | |
{Binding ElementName=transform, Path=Children[0].Angle} | インデクサ参照 | |
DataContextとして、 {Binding Children, ElementName=transform} を渡すものとして、 {Binding [0].Angle} |
ソース自体がコレクションの場合、[]から書き始めることでインデクサ参照できる | |
{Binding ElementName=check, Path=Content.RenderTransform .(TransformGroup.Children)[0] .Angle} |
構文を組み合わせた例 |
Copyright© Digital Advantage Corp. All Rights Reserved.