連載
» 2010年10月08日 00時00分 公開

連載:WPF入門:第5回 WPFの「データ・バインディング」を理解する (3/3)

[岩永信之(http://ufcpp.net/),著]
前のページへ 1|2|3       

データ・テンプレート

 文字列や数値などの単純な型だけでなく、任意のデータ型を表示するための仕組みとして、データ・テンプレートというものがある。前回で説明したコントロール・テンプレートと似ているが、名前どおり、コントロール・テンプレートがコントロールの表示方法をカスタマイズするものであるのに対して、データ・テンプレートはデータの表示方法をカスタマイズするものである。

ContentControlクラス:単一のデータに対するデータ・テンプレートの適用

 まず、単一の(コレクションではない)データに対するテンプレートについて説明しよう。単一データに対するテンプレート指定は、ContentControlクラス(Sytem.Windows.Control名前空間)のContentプロパティにデータを、ContentTemplateプロパティにデータ・テンプレートを渡すことによって行う*

* ちなみに、Buttonクラスなどのコントロール類の多くはContentControlクラスを継承していて、このデータ・テンプレートの仕組みを利用可能である。


 ここでは、標準のクラス・ライブラリで提供されているデータ型の中から、式ツリー(System.Linq.Expressions名前空間以下のクラス)を例にとって説明していく。まず、List 12に示すように、「(x, y) => x + y」という(ラムダ式の)式ツリーをDataContextプロパティに渡す。

System.Linq.Expressions.Expression<Func<int, int, int>> sample =
  (x, y) => x + y;
this.DataContext = sample;

Dim sample As System.Linq.Expressions.Expression( _
  Of Func(Of Integer, Integer, Integer)) = _
    Function(x, y) x + y
Me.DataContext = sample

List 12: 式ツリーをデータ・ソースにする例(上:C#、下:VB)

 比較のために、テンプレート指定のない場合にどうなるかを見てみよう。List 13に示すように、Contentプロパティのみを指定する。ソース・プロパティとして指定しているBodyプロパティには、ラムダ式の本体部分(=「x + y」の部分)が格納されている。

……省略……
<StackPanel>
  <ContentControl Content="{Binding Body}" />
</StackPanel>
……省略……

List 13: データ・テンプレートを指定しない場合(XAML)
データは、<ContentControl>要素のContentプロパティに指定する。

 この場合、ただ単に「(x + y)」という文字列だけが表示されるはずだ。これは、式ツリーをToStringメソッドで文字列化したものが表示されている。

 それでは、データ・テンプレートを指定してみよう。

 List 14に示すように、ContentTemplateプロパティに対して、<DataTemplate>要素を指定する。Figure 8に表示結果を示す。データ・テンプレートを指定しなかった場合もまとめて表示している。図中の上段が指定なし、下段が指定ありの場合の表示結果である。

……省略……
<Grid>
  <ContentControl Content="{Binding Body}"
                  Grid.Row="1" Grid.Column="1">

    <ContentControl.ContentTemplate>

      <DataTemplate>
        <StackPanel Orientation="Horizontal">

          <TextBlock Text="{Binding NodeType}"
                     Background="LightBlue" />

          <TextBlock Text=": " />

          <TextBlock Text="{Binding Left}"
                     Background="LightBlue" />

          <TextBlock Text=", " />

          <TextBlock Text="{Binding Right}"
                     Background="LightBlue" />

        </StackPanel>
      </DataTemplate>

    </ContentControl.ContentTemplate>

  </ContentControl>
</Grid>
……省略……

List 14: データ・テンプレートを指定する場合(XAML)
データ・テンプレートは、<ContentControl>要素のContentTemplateプロパティに指定する。

Figure 8: List 13とList 14の表示結果

データ・テンプレートの自動適用

 データ・テンプレートは、もちろんリソース化して複数の<ContentControl>要素間で共有できる。また、コントロール・テンプレートで自動適用ができたように(具体的には、<ControlTemplate>要素にTargetTypeプロパティを設定)、データ・テンプレートも<DataTemplate>要素のDataTypeプロパティを設定することで、指定した型に対してデータ・テンプレートを自動適用できる。

 例えば、List 14の例は、List 15のように書き換えられる。

……省略……
<Grid xmlns:exp="clr-namespace:System.Linq.Expressions;assembly=System.Core">
  <Grid.Resources>

    <DataTemplate DataType="{x:Type exp:BinaryExpression}">
      <StackPanel Orientation="Horizontal">

        <TextBlock Text="{Binding NodeType}"
                   Background="LightBlue" />

        <TextBlock Text=": " />

        <TextBlock Text="{Binding Left}"
                   Background="LightBlue" />

        <TextBlock Text=", " />

        <TextBlock Text="{Binding Right}"
                   Background="LightBlue" />

      </StackPanel>
    </DataTemplate>

  </Grid.Resources>
 
  <ContentControl Content="{Binding Body}" />
</Grid>
……省略……

List 15: データ・テンプレートの自動適用の例(XAML)
データ・テンプレートをリソース化して<DataTemplate>要素のDataTypeプロパティを設定することで、指定した型に対して一括でデータ・テンプレートを自動適用できる。この例では、System.Coreアセンブリに含まれるBinaryExpression型(System.Linq.Expressions名前空間)のデータすべてに対して、<DataTemplate>要素で定義されたデータ・テンプレートが自動的に適用される。

 データ・テンプレートの自動適用は、型の混在する階層的なデータに対してテンプレートを適用したいときに特に重宝することだろう。

 例えば、式ツリーは、その名前どおり、階層的なデータ構造をしているので、List 15に例示したBinaryExpressionクラスなら、LeftプロパティやRightプロパティも式ツリーになっていて、BinaryExpressionクラスをはじめとするさまざまなクラスが格納されている。このとき、Leftプロパティなどを再度<ContentControl要素>のContentプロパティにバインディングしておけば、階層的なテンプレート適用が行われる。

 式ツリーを構成するさまざまな型すべてに対してデータ・テンプレートを適用できるリソース・ファイルを、下記のURLからダウンロードできるようにしたので、興味がある場合にはダウンロードしてみていただきたい。リソース・ファイルの利用方法は、本連載第4回の「外部リソースの取り込み」の項を参照してほしい。

 このデータ・テンプレート一式を使って、

(int x, int y) => (x + 3) * (y - 1)

というラムダ式から得られた式ツリー全体を表示すると、Figure 9のようになる。

Figure 9: 階層的なデータに対してデータ・テンプレートを適用した例
ここまでの実行例では「{Binding Body}」としてラムダ式の本体部分の式ツリー(=Bodyプロパティ)のみを表示していたが、この例では「{Binding}」(=「{Binding Path=.}」とも記述可能)としてラムダ式全体(=バインディング・ソース)の式ツリーを表示している。

ItemsControlクラス:コレクションに対するデータ・テンプレートの適用

 データ・ソースがコレクションの場合、ItemsControlクラス(Sytem.Windows.Control名前空間)のItemsSourceプロパティにデータを、ItemTemplateプロパティにデータ・テンプレートを渡すことによってデータ・バインディングを行う。ItemTemplateプロパティに指定したテンプレートは、コレクションの要素1つ1つに対して適用される。また、この場合にも、(単一のデータの場合と同様に)データ・テンプレートの自動適用は有効である。

 List 12と同じデータ(「x + y」という式ツリー)に対して、List 16に示すようなXAMLコードを書くと、Figure 10のような表示結果が得られる。ソース・プロパティとして指定しているParametersプロパティには、ラムダ式の引数リストの部分(=「x, y」の部分)が格納されていて、これはParameterExpression型のリストになっている。各要素には、型に基づいてテンプレートが自動適用される。

……省略……
<Grid xmlns:exp="clr-namespace:System.Linq.Expressions;assembly=System.Core">
  <Grid.Resources>

    <DataTemplate DataType="{x:Type exp:ParameterExpression}">
      <StackPanel Orientation="Horizontal">

        <TextBlock Text="{Binding Name}"
                   Background="LightPink" />

        <TextBlock Text=":" />

        <TextBlock Text="{Binding Type.Name}"
                   Background="LightPink" />

      </StackPanel>
    </DataTemplate>

  </Grid.Resources>

  <ItemsControl ItemsSource="{Binding Parameters}" />
</Grid>
……省略……

List 16: コレクションに対するデータ・テンプレートの適用(XAML)
データは<ItemsControl>要素のItemSourceプロパティに指定する。データ・テンプレートは、個々の<ItemsControl>要素のItemTemplateプロパティに指定することもできるが、この例では、データ・テンプレートをリソース化して<DataTemplate>要素のDataTypeプロパティを設定することで、指定した型に対して一括でデータ・テンプレートを自動的に適用している。

Figure 10: List 16の表示結果

【コラム】C#コード内でデータ・バインディング

 参考までに、データ・バインディングをC#コードだけで記述する方法に触れておこう。

 「<TextBlock Text="{Binding Path=X, Mode=OneWay}" />」という記述と同様のデータ・バインディングをC#コード内で行うためには、List 17に示すような記述が必要になる。Bindingクラス(System.Windows.Data名前空間)のインスタンスを作成し、FrameworkElementクラスのSetBindingメソッドを通じて、データ・バインディングを行う。

var source = new { X = 10, Y = 20 };

var binding = new Binding();
binding.Source = source;
binding.Mode = BindingMode.OneWay;
binding.Path = new PropertyPath("X");

var target = new TextBlock();
target.SetBinding(TextBlock.TextProperty, binding);

List 17: C#コードのみでデータ・バインディングを行う例



 次回はコマンドに関する説明を行う。また、MVVMパターンと呼ばれる、データ・バインディングとコマンドを利用して、ビューから状態を切り離す手法に関しても説明を行っていく。

「連載:WPF入門」のインデックス

連載:WPF入門

前のページへ 1|2|3       

Copyright© Digital Advantage Corp. All Rights Reserved.

編集部からのお知らせ

8月8日10時30分〜16時30分の間、システムメンテナンスのため記事の一部表示や資料のダウンロードができなくなります。ご理解のほどよろしくお願いいたします。

RSSについて

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

メールマガジン登録

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