UIレイアウトの次は、業務アプリには不可欠の「データの表示と入力」の実装だ。それを簡単に実現できるデータ・バインディングについて解説。
powered by Insider.NET
第1回となる前回は、レイアウトの基本であるPanel(=Panelクラスの派生クラスにマッピングされた<Canvas>要素、<StackPanel>要素、<Grid>要素など)と、そこに配置可能なコントロールを紹介した。レイアウトを決め、UI要素であるコントロールを配置したら、次に必要となるのは何だろうか? 筆者が考える次のステップは、「データの表示と入力」である。
業務系アプリケーションであれば、データの表示と入力という要素は必要不可欠なはずだ。そして、その実装の際に開発者の強力な味方となってくれるのがデータ・バインディング(以下、バインディング)である。バインディングを使うことで得られるメリットは後ほど紹介することにして、まずはWPF UIフレームワーク(=WPF/Silverlight共通のUIフレームワーク)におけるバインディングとはどのようなものなのか、といった部分から見ていくことにしよう。
●データ・バインディングの基本
WPF UIフレームワークにおけるバインディングとは、2つの異なるオブジェクトのプロパティ値を同期させる機能のことである。下の図のように、主にUI要素とデータ・オブジェクトとの間でこのバインディングが用いられる。
WPF UIフレームワークのバインディングやアニメーションなどでは、結合されるデータ・オブジェクトの入力値に基づいた(UI要素の)プロパティ値を算出するために新しいプロパティ・システムが採用されている。
これは依存関係プロパティと呼ばれるもので、バインディング・ターゲット(=UI要素)で使用できるプロパティは依存関係プロパティのみであり、WPF UIフレームワークにおけるUI要素が持つプロパティの多くが依存関係プロパティとして実装されている。
依存関係プロパティは、プライベート・フィールドで値を管理する通常のプロパティ(=CLRプロパティ)と異なり、WPF/Silverlightプロパティ・システムが値を管理している。ただし、CLRプロパティに対する互換性や使い勝手の問題から、依存関係プロパティにはCLRラッパーが必ず用意されており、CLRプロパティと同様の方法で使用することが可能となっている。
バインディングの機能は、Bindingクラスによって提供される。以下に、Bindingクラスが持つ主要なプロパティとその役割を示す。
プロパティ名 | |
---|---|
Path | バインディング・ソースのプロパティへのパス |
Source | バインディング・ソースとなるオブジェクト |
Mode | データが反映される方向 |
Bindingクラスの主要プロパティ |
BindingオブジェクトのModeプロパティに設定可能なBindingMode列挙値は、下記の表のとおりである。
BindingMode列挙値 | |
---|---|
OneWay | バインディング・ソースのデータ(=プロパティ値)が変更されると、バインディング・ターゲットのデータ(=プロパティ値)を更新 |
OneTime | アプリケーションの起動時にのみ、バインディング・ターゲットのデータを更新 |
TwoWay | バインディング・ソースまたはバインディング・ターゲットのどちらか一方のデータが変更されると、もう一方も自動的に更新 |
BindingMode列挙体 WPFでは、これらに加えてOneWayの逆方向となる「OneWayToSource」と、バインディング・ターゲットの既定のModeとなる「Default」が存在する。バインディング・ターゲットのModeの既定値は、依存関係プロパティごとに定義されている。 |
●一方向データ・バインディング
では、まずは一方向のバインディングから見ていこう。この場合の全体の構成は下の図のようになる。
ここでは、バインディング・ソース(以下、ソース)となるオブジェクトとして、次のようなHeightとWeightという2つのdouble型プロパティを持つPersonクラスを作成する。
class Person
{
public double Height { get; set; }
public double Weight { get; set; }
}
Public Class Person
Private _height As Double
Private _weight As Double
Public Property Height() As Double
Get
Return _height
End Get
Set(ByVal value As Double)
_height = value
End Set
End Property
Public Property Weight() As Double
Get
Return _weight
End Get
Set(ByVal value As Double)
_weight = value
End Set
End Property
End Class
次にバインディング・ターゲット(以下、ターゲット)となるUI部分(XAMLコード)だが、これは下記のリストのようにStackPanelにTextBlockコントロールを2つ配置したものを使用する。
今回はUIのコードビハインド部分(C#/VBコード)からBindingオブジェクトの設定を行うため、StackPanelと各TextBlockコントロールにx:Name属性を使って名前を付け、StackPanelのLoadedイベントに対してMainPanel_Loadedというハンドラを登録しておく。
<StackPanel x:Name="MainPanel" Loaded=" MainPanel_Loaded">
<TextBlock x:Name="TextBlock1"/>
<TextBlock x:Name="TextBlock2"/>
</StackPanel>
そして最後に、UIのコードビハインド部分で、Bindingオブジェクトを使ってソースとターゲットの関連付けを行う。
private void MainPanel_Loaded(object sender, RoutedEventArgs e)
{
Person BindingSource =
new Person() { Height = 1.7, Weight = 60 };
Binding BindingHeight = new Binding("Height");
BindingHeight.Mode = BindingMode.OneWay;
BindingHeight.Source = BindingSource;
TextBlock1.SetBinding(TextBlock.TextProperty, BindingHeight);
Binding BindingWeight = new Binding("Weight");
BindingWeight.Mode = BindingMode.OneWay;
BindingWeight.Source = BindingSource;
TextBlock2.SetBinding(TextBlock.TextProperty, BindingWeight);
}
Private Sub MainPanel_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim BindingSource = _
New Person() With {.Height = 1.7, .Weight = 60}
Dim BindingHeight As New Binding("Height")
BindingHeight.Mode = BindingMode.OneWay
BindingHeight.Source = BindingSource
TextBlock1.SetBinding(TextBlock.TextProperty, BindingHeight)
Dim BindingWeight As New Binding("Weight")
BindingWeight.Mode = BindingMode.OneWay
BindingWeight.Source = BindingSource
TextBlock2.SetBinding(TextBlock.TextProperty, BindingWeight)
End Sub
Bindingクラスのコンストラクタには、引数なしのコンストラクタに加え、引数としてプロパティ名を受け取って、自身のPathプロパティにその値を設定するオーバーロードが用意されている。もちろん、引数なしのコンストラクタを使用し、直接Pathプロパティを設定する方法でも動作に違いはない。
Silverlightの場合、Modeプロパティの既定値は「OneWay」だ。これに対してWPFでは、「Default」(=ターゲットとなるUI要素ごとに定義されている既定値)がModeプロパティの既定値であるため、ターゲットとなるプロパティによって既定のデータフロー方向が異なってくる。例えば、表示のみに使われるTextBlock.Textプロパティの場合、既定のデータフロー方向はOneWayとなるが、表示だけでなく入力も行われるTextBox.Textプロパティの場合にはTwoWayとなる。トラブルを避けるためにもModeプロパティは明示的に設定しておいた方がよいだろう。
BindingオブジェクトのSourceプロパティには、ソースとしてHeightプロパティとWeightプロパティに初期値を設定したPersonオブジェクトを設定している。その後、SetBindingメソッドを使ってTextBlockコントロールのTextプロパティに対してBindingオブジェクトを関連付けている。
実行結果はとてもシンプルだ。下の画面のように、Personオブジェクトの各プロパティに設定した1.7と60という値が、文字列としてTextBlockコントロールに表示される。
バインディングでは、ターゲットとソースでプロパティの型が異なる場合、相互に暗黙的な型変換が行われる。今回の場合、ターゲット側はString型、ソース側はDouble型となっており、実際に暗黙的な型変換が行われていることを確認できる。なお、型変換できない場合、バインディングはnull(VBではNothing)参照を返す。
本当に最小の構成ではあるが、以上がWPF UIフレームワークのバインディングの基本的な実装である。
続いて、より実践的なバンディング方法を説明していこう。
Copyright© Digital Advantage Corp. All Rights Reserved.