第2回 データの表示と入力に必要な知識連載 WPF/Silverlight UIフレームワーク入門(1/5 ページ)

UIレイアウトの次は、業務アプリには不可欠の「データの表示と入力」の実装だ。それを簡単に実現できるデータ・バインディングについて解説。

» 2009年05月19日 00時00分 公開
[八巻雄哉グレープシティ株式会社]
連載 WPF/Silverlight UIフレームワーク入門
業務アプリInsider/Insider.NET

powered by Insider.NET

「連載 WPF/Silverlight UIフレームワーク入門」のインデックス

連載目次

■データ・バインディング

 第1回となる前回は、レイアウトの基本であるPanel(=Panelクラスの派生クラスにマッピングされた<Canvas>要素、<StackPanel>要素、<Grid>要素など)と、そこに配置可能なコントロールを紹介した。レイアウトを決め、UI要素であるコントロールを配置したら、次に必要となるのは何だろうか? 筆者が考える次のステップは、「データの表示と入力」である。

 業務系アプリケーションであれば、データの表示と入力という要素は必要不可欠なはずだ。そして、その実装の際に開発者の強力な味方となってくれるのがデータ・バインディング(以下、バインディング)である。バインディングを使うことで得られるメリットは後ほど紹介することにして、まずはWPF UIフレームワーク(=WPF/Silverlight共通のUIフレームワーク)におけるバインディングとはどのようなものなのか、といった部分から見ていくことにしよう。

データ・バインディングの基本

 WPF UIフレームワークにおけるバインディングとは、2つの異なるオブジェクトのプロパティ値を同期させる機能のことである。下の図のように、主にUI要素とデータ・オブジェクトとの間でこのバインディングが用いられる。

WPF UIフレームワークにおけるバインディング
バインディング・ソース(=結合元)とバインディング・ターゲット(=結合先)を、Bindingオブジェクトによって結合する。結合方式には「OneWay」「TwoWay」「OneTime」の3種類があり、BindingオブジェクトのModeプロパティにより指定できる(詳細後述)。

【コラム】依存関係プロパティ

 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の既定値は、依存関係プロパティごとに定義されている。

一方向データ・バインディング

 では、まずは一方向のバインディングから見ていこう。この場合の全体の構成は下の図のようになる。

Sourceプロパティを使用した一方向(OneWay)のバインディング
PersonオブジェクトのHeightプロパティとWeightプロパティを、異なる2つのTextBlockコントロールにバインドしている。

 ここでは、バインディング・ソース(以下、ソース)となるオブジェクトとして、次のような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

ソースとして利用するPersonクラス(上:C#、下:VB)

 次にバインディング・ターゲット(以下、ターゲット)となる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部分(XAML)

 そして最後に、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オブジェクトを使ってターゲットとソースの関連付けを行うコード(上:C#、下:VB)
BindingSourceがソース、TextBlock1とTextBlock2がターゲット、BindingHeightとBindingWeightがBindingオブジェクト。
※Silverlightプロジェクトでは、System.Windows.Data名前空間のインポートが必要。

 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フレームワークのバインディングの基本的な実装である。

 続いて、より実践的なバンディング方法を説明していこう。

       1|2|3|4|5 次のページへ

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。