マイクロソフト系技術情報 Wiki」は、「Open棟梁Project」,「OSSコンソーシアム .NET開発基盤部会」によって運営されています。

目次

概要

基本的なXAMLの書き方。

名前空間

XAMLにおける各種の名前空間の宣言は、xmlns属性を使用したXML名前空間にて行う。
ここでは、以下の既定の名前空間の宣言を例にとって説明する。

  • 名前空間の宣言
    <Window x:Class="WpfApplication1.Window1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

WPF名前空間

2行目のXML名前空間の宣言(xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation")では、
WPFフレームワーク(PresentationFramework?.dll)のアセンブリ内に同梱されるURIにマップされた
CLR名前空間(System.Windows.Controls、System.Windows.Dataなど)を、既定のXML名前空間(プレフィックスなし)として割り当てている。
そのため、既定でXAMLからWPFフレームワークのCLRオブジェクトを利用できる。

XAML名前空間

3行目のXML名前空間の宣言(xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml")では、
URIにマップされた共通的なXAMLの言語機能がXML名前空間(x)として割り当てられている。
これにより、「x:」というプレフィックスを使用することで、「言語機能」で説明する、XAMLの言語機能を使用できるようになる。

CLR名前空間

CLR名前空間について以下を例にとって説明する。

(1)

  • CLR名前空間の宣言
    <Window x:Class="WpfApplication1.Window1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:igDP="http://infragistics.com/DataPresenter" <--- 名前空間の宣言(追加
      Title="Window1" Height="300" Width="300">
      <Grid>
        <igDP:XamDataGrid Name="xamDataGrid1"/> <--- 名前空間の使用例
      </Grid>
    </Window>

Infragistics社製のNetAdvantage?など、

XmlnsDefinition?アセンブリ属性でURIとCLR名前空間のマップが指定された
サードパーティ製のUIコンポーネントをD&DでVSデザイナから追加した場合、

上記の例のように自動的にXML名前空間の宣言が追加される。

なお、XML名前空間には一意の名前を自由に付与でき(上記の例ではigDP)、
このプレフィックスを使用することで、XAMLからUIコンポーネントのCLRオブジェクトを利用できる。

(2)

この他に、URIとしてCLR名前空間とアセンブリを直接指定する方法もある。

  • CLR名前空間の宣言
    xmlns:sys="clr-namespace:(CLR名前空間);assembly=(アセンブリ名)"
    • 例1:サードパーティ製品
      <Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:igDP="clr-namespace:Infragistics.Windows.DataPresenter;assembly=Infragistics3・・・" <--- 名前空間の宣言
        Title="Window1" Height="300" Width="300">
        <Grid>
          <igDP:XamDataGrid Name="xamDataGrid1"/> <--- 名前空間の使用例
    • 例2:.NET Framework
      <Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib" <--- 名前空間の宣言
        Title="Window1" Height="300" Width="300">
        <Window.Resources>
          <x:Array x:Key="List" Type="{x:Type sys:String}"> <--- 名前空間の使用例
    • 例3:自作の同一プロジェクトのクラス
      <Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:uc="clr-namespace:WpfApplication1" <--- 名前空間の宣言
        Title="Window1" Height="300" Width="300">
        <StackPanel Orientation="Vertical">
          <uc:UserControl1 x:Name="userControl1"/> <--- 名前空間の使用例
  • この方法では、「clr-namespace」、「assembly」などの定義済みトークンを使用する。
    • 当該プロジェクト中のCLR名前空間を指定する場合は、「assembly」トークンは省略できる。
    • XML名前空間には一意の名前を自由に付与でき、このプレフィックスを使用することで、XAMLからUIコンポーネントのCLRオブジェクトを利用できる。
  • 製品並みのカスタムのUIコントロールを開発・使用するなどの目的を除いて、CLRクラスを開発しXAMLから使用する場合、
    • 一般的には「clr-namespace、assembly」の定義済みトークンを使用する。
    • ただし、この方法は、1つのCLR名前空間に対して、1つのXML名前空間しか割り当てられない
    • XmlnsDefinition?アセンブリ属性を使用すると、1つのXML名前空間に複数のCLR名前空間を割り当てられる)。

参考

言語機能

XAMLの言語機能である

について説明する。

ディレクティブ

XAMLの言語機能が提供する各種ディレクティブについて説明する。

項番ディレクティブ説明
1x:ClassXAML上から分離クラス(コード ビハインド)のクラス名を定義する。
<Window x:Class="WpfApplication1.Window1"
2x:Subclassパーシャル クラスをサポートしない言語で使用する。通常は利用しない。
3x: ClassModifier?クラスのアクセスレベルを変更する。通常は利用しない。
4x:CodeXAML上にインラインコードを実装する場合に使用する。通常は利用しない。
<Grid>
  <x:Code>
    <![CDATA[
      void button1_Click(object sender, RoutedEventArgs? e) {
        button1.Content = "Hello World";
      }
    ]]>
  </x:Code>
  <Button Name="button1" Click="button1_Click">Button</Button>
</Grid>
5x:FieldModifier?プロパティのアクセスレベルを変更する。通常は利用しない。
6x:KeyXAMLで定義された各種「リソース」を識別する。下記は、x:Keyを使用して「スタイル」の「リソース」をボタンに「データ バインディング」する例。
<Window.Resources>
  <Style x:Key="buttonStyle" TargetType?="{x:Type Button}">
    <Setter Property="Background" Value="LightYellow?" />
  </Style>
</Window.Resources>
<Grid>
  <Button Style="{StaticResource? buttonStyle}">Hello Style</Button>
</Grid>
7x:NameXAMLで生成したCLRオブジェクトに名前を付与する。Name属性と差異は無い。
<Button x:Name="button1">
   Click Here
</Button>
8x:Shared静的なリソースを、取得の度に生成する場合に使用する。通常は利用しない。
※ true : 静的(全てのインスタンスは同じ)
   false : 動的(取得の度に生成する)
   既定値 : true
9x:TypeArguments?ジェネリックの型引数をコンストラクタに渡す。
(.NET Framework 4.0のXAML 2009からのサポート)
<!-- XAML 2009 -->
<ObservableCollection? x:TypeArguments?="Employee">
  <l:Employee FirstName?="John" Name="Doe" />
  <l:Employee FirstName?="Tim" Name="Smith" />
</ObservableCollection?>
10x:Uidローカライゼーションのプロセスとツールによって使用される一意識別子を指定する 。
11xml:langカルチャ情報を宣言する。
<Window x:Class="WpfApplication1.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xml:lang="ja-JP"

マークアップ拡張

XAMLの言語機能が提供する各種「マークアップ拡張」について説明する。
通常、「マークアップ拡張」は、「{」と「}」の2つの中括弧を使用することでXAMLパーサに対し、拡張されたプロパティ指定方法を指示する。

XAMLで定義されたマークアップ拡張

以下、XAMLの機能である「XAMLで定義されたマークアップ拡張」を一覧する。
これらの種類は、中括弧+「x:」プレフィックスの直後の文字列トークンによって識別される。

  • XAMLに実装されたマークアップ拡張
    項番文字列トークン説明
    1x:Static静的プロパティ(定数、静的プロパティ、フィールド、列挙値)を参照する。
    構文:<object property="{x:Static Member=prefix:typeName.staticMemberName?}" .../>
    <Button 
        Foreground="{x:Static Member=SystemColors?.InfoTextBrush?}"
        Background="{x:Static Member=SystemColors?.InfoBrush?}">
      Click Here
    </Button>
    2x:NullCLRオブジェクトのプロパティにnull値を設定する(既定値をnullクリアする場合など)。
    <Button x:Name="button1" Background="{x:Null}" Click="button1_Click">
      Click Here
    </Button>
    3x:TypeCLRクラスの型情報を指定する。
    詳しくは、下記、項番4の例を参照のこと。
    4x:ArrayIEnumerableを持つArrayオブジェクト(配列)を生成する。
    <Window.Resources>
      <x:Array x:Key="List"
        Type="{x:Type sys:String}">
        <sys:String>A</sys:String>
        <sys:String>B</sys:String>
        <sys:String>C</sys:String>
      </x:Array>
    </Window.Resources>
    <StackPanel?>
      <ListBox? ItemsSource?="{Binding Source={StaticResource? List}}"/>
    </StackPanel?>

    ※ 先頭で、.NET FrameworkのSystem名前空間のインポートが必要
       xmlns:sys="clr-namespace:System;assembly=mscorlib"

    ※ StaticResource?については次項で説明する。

※ x:Arrayは、例外的に中括弧と共に使用しない。

WPF固有のマークアップ拡張

以下、WPF の機能である「WPF固有のマークアップ拡張」を一覧する。
こちらは、プロパティ値に「データ バインディング」や、「リソース」への参照を指定できる。

※ keyには、x:Keyディレクティブによって指定された既存のリソースに対応するキーを指定する。

マークアップ拡張の例

バインディングのマークアップ拡張の例

こちら

TemplateBinding?の例

こちら

RelativeSource?の例

こちら

プロパティの設定方法

プロパティの設定方法として、

の2つの構文について説明する。

以下、TextBox?要素に対して、同等の属性を設定する
プロパティ属性構文」と「プロパティ要素構文」の例を示す。

プロパティ属性構文

要素の属性にテキストを使用してプロパティを設定する方法

  • プロパティ属性構文の例
    <TextBox 
       Width = "100"
       FontSize = "30"
       Text = "text1"
       Background = "White"
       Foreground = "Blue" />

プロパティ要素構文

要素のinnerText・innerXMLを使用してプロパティを設定する方法

  • プロパティ要素構文の例
    <TextBox>
      <TextBox.Width>100</TextBox.Width>
      <TextBox.FontSize>30</TextBox.FontSize>
      <TextBox.Background>
        <SolidColorBrush Color="White"/>
      </TextBox.Background>
      <TextBox.Foreground>
        <SolidColorBrush Color="Blue"/>
      </TextBox.Foreground>
      <TextBox.Text>text1</TextBox.Text>
    </TextBox>

なお、子要素のコレクションを設定する「プロパティ要素構文」である

などは、暗黙的に使用されるため明記が不要のものもある。

型コンバータ

XAMLパーサは、XAMLの「プロパティ属性構文」として指定された各属性テキストを、
プリミティブ型に変換できるリテラル文字列として解釈するか、「型コンバータ」を使用してオブジェクトに変換する。

なお、「型コンバータ」(と、そのベース クラスであるTypeConverter?クラス)は、
.NETのコンポーネントとコントロールのデザイン時・実行時の動作を実装する一般的なクラスであり、WPF独自のクラスではない。
しかし、XAMLの「プロパティ属性構文」からのCLRプロパティ設定を多用するWPF / Silverlight開発では、その存在を認識しておいたほうが良い。

XAMLパーサは通常、プロパティの型(CLRクラス)にTypeConverterAttribute?属性 が付与されているかを調べる。
付与されている場合は、この属性値に基づいた「型コンバータ」を使って、TypeConverter?.ConvertFrom?メソッド により文字列をプロパティ値に変換する。

以下は、ユーザ コントロールに、MyPoint?というカスタムの型を取るCLRプロパティを実装しXAMLに公開、
XAMLの「プロパティ属性構文」として指定された各属性テキストを、カスタムの型に変換する「型コンバータ」を実装した例である。

カスタム型

MyPoint?というカスタム型(型コンバータを実装)

/// <summary>
/// カスタム型(型コンバータを実装)
/// </summary>
[TypeConverter(typeof(MyPointConverter))]
public class MyPoint {
  public MyPoint(int x, int y) {
    this._x = x;
    this._y = y;
  }
  private int _x;
  public int X {
    set { this._x =value; }
    get { return this._x; } 
  }
  private int _y;
  public int Y {
    set { this._y = value; }
    get { return this._y; }
  }
}

型コンバータ

/// <summary>カスタム型の型コンバータ</summary>
public class MyPointConverter : TypeConverter {

  /// <summary>CanConvertFrom(変換可能かチェックする)</summary>
  public override bool CanConvertFrom(
    ITypeDescriptorContext context, Type sourceType) {
    if (sourceType == typeof(string)) {
      return true;
    }
    return base.CanConvertFrom(context, sourceType);
  }

  /// <summary>指定された文字列をカスタム型(MyPoint)に変換する</summary>
  public override object ConvertFrom(
    ITypeDescriptorContext context,
    CultureInfo culture, object value) {
    if (value is string) {
      string[] v = ((string)value).Split(new char[] { ',' });
      return new MyPoint(int.Parse(v[0]), int.Parse(v[1]));
    }
    return base.ConvertFrom(context, culture, value);
  }

  /// <summary>指定されたカスタム型(MyPoint)を文字列に変換する</summary>
  public override object ConvertTo(ITypeDescriptorContext context,
    CultureInfo culture, object value, Type destinationType) {
    if (destinationType == typeof(string)) {
      return ((MyPoint)value).X + "," + ((MyPoint)value).Y;
    }
    return base.ConvertTo(context, culture, value, destinationType);
  }
}

ユーザ コントロール

MyPoint?というカスタム型のCLRプロパティを実装

/// <summary>
/// UserControl1.xaml の相互作用ロジック
/// </summary>
public partial class UserControl1
  : UserControl {
  // CLRプロパティの定義(XAML属性に公開可能)
  private MyPoint _myLocation;

  public MyPoint MyLocation {
    set { this._myLocation = value; }
    get { return this._myLocation; }
  }

・・・

型コンバータの使用例

上記のユーザ コントロールのカスタムの型を取るCLRプロパティを、以下のようにXAMLの「プロパティ属性構文」として指定された各属性テキストから初期化できる。

<uc:UserControl1 x:Name="userControl1" MyLocation="10,20"/>

ただし、(当然ながら、)文字列からの変換をサポートしているだけで、すべてのプロパティ値をサポートすることはできない。
文字列で表現できないプロパティ値は、「プロパティ属性構文」ではなく「プロパティ要素構文」として記述する方法を取る。

コンテンツ構文

「コンテンツ構文」とは、Contentプロパティ(またはItemsプロパティ)に要素を設定する構文である。

  • ContentPropertyAttribute?を付けることで、
  • 比較
    • なお、Windows FormsWeb Forms(HTML)などで、UIコントロールの表示のカスタマイズをするには、
      UIコントロールの「スタイル」属性の指定による方法のみサポートされていた。
    • これに対し、WPFのUIコントロールは「スタイル」属性の指定による方法だけでなく、
      Contentプロパティ(またはItemsプロパティ)に任意の型の子要素を設定することができるため、
      コントロールの「外観」を自由に変更でき、柔軟性が非常に高くなっている。
  • 補足:Windowsフォームの外観の変更
    • コントロールを自前で描画する「オーナー描画」と呼ばれる方法が用意されていたが、
      「オーナー描画」はグラフィックス メソッドなどを使用して、
      すべての描画を独自に行わなければならないため、深い知識と多量のコードが必要であった。

Contentプロパティ

WPFのContentControl?は、

  • ContentControl?.Contentプロパティに設定された単一の要素を表示するという機能を提供するコントロール
  • ContentControl?.Contentプロパティに任意の型の子要素を設定することができるため、コントロールの「外観」を自由に変更できる。
  • 従って、ContentControl?を継承するButtonクラスやLabelクラスなどなどではコントロールの「外観」を自由に変更できる。
    • Control.Templateプロパティ
    • ContentControl?.ContentTemplate?プロパティ

文字列のコンテンツ要素を格納

WPFのContentControl?のButtonコントロールを例にしてContentプロパティへ要素を設定する例を示す。

イメージのコンテンツ要素を格納

Contentプロパティには、Imageオブジェクトなどの任意の型の要素も設定できる。

  • XAML
    <Button Width="200" Height="200">
      <Button.Content>
        <Image Source=".\Blue hills.jpg"/>
      </Button.Content>
    </Button>
  • レンダリング結果
    イメージのコンテンツ要素を格納

パネルに纏めて格納した複数のコンテンツ要素

  • XAML
    • 2つ以上の要素を設定したい場合は、パネル要素を使用する。
      <Button Width="200" Height="200">
        <Button.Content>
          <StackPanel Orientation="Vertical">
            <Image Source=".\Blue hills.jpg" Margin="5" />
            <TextBlock Text="ボタン" HorizontalAlignment="Center" Margin="5"/>
          </StackPanel>
        </Button.Content>
      </Button>
    • なお、<Object.Content>タグは省略することもできる。
      <Button Width="200" Height="200">
        <StackPanel Orientation="Vertical">
          <Image Source=".\Blue hills.jpg" Margin="5" />
          <TextBlock Text="ボタン" HorizontalAlignment="Center" Margin="5"/>
        </StackPanel>
      </Button>
  • レンダリング結果
    パネルに纏めて格納した複数のコンテンツ要素

Itemsプロパティ

WPFのItemsControl?は、Itemsプロパティに任意の型の子要素を設定することができるため、コントロールの「外観」を自由に変更できる。

複数のパネルに纏めて格納した複数のコンテンツ要素

ItemsControl?のItemsプロパティへは、複数のコンテンツを追加できる。
以下、ListBox?コントロールを例にしてItemsプロパティへ子要素の設定する例を示す。

  • XAML
    <Grid>
      <ListBox>
        <Border BorderBrush ="Black" BorderThickness ="1" Margin="5">
          <StackPanel Orientation="Horizontal" Height="120" Width="250" Margin="5">
            <Image Source=".\Blue hills.jpg" Height="100"/>
            <TextBlock Text="Blue hills" VerticalAlignment="Center"/>
          </StackPanel>
        </Border>
        <Border BorderBrush ="Black" BorderThickness ="1" Margin="5">
          <StackPanel Orientation="Horizontal" Height="120" Width="250" Margin="5">
            <Image Source=".\Sunset.jpg" Height="100"/>
            <TextBlock Text="Sunset" VerticalAlignment="Center"/>
          </StackPanel>
        </Border>
        <Border BorderBrush ="Black" BorderThickness ="1" Margin="5">
          <StackPanel Orientation="Horizontal" Height="120" Width="250" Margin="5">
            <Image Source=".\Water lilies.jpg" Height="100"/>
            <TextBlock Text="Water lilies" VerticalAlignment="Center"/>
          </StackPanel>
        </Border>
        <Border BorderBrush ="Black" BorderThickness ="1" Margin="5">
          <StackPanel Orientation="Horizontal" Height="120" Width="250" Margin="5">
            <Image Source=".\Winter.jpg" Height="100"/>
            <TextBlock Text="Winter" VerticalAlignment="Center"/>
          </StackPanel>
        </Border>
      </ListBox>
    </Grid>
  • レンダリング結果
    複数のパネルに纏めて格納した複数のコンテンツ要素

リソース

ここでは、

  • 「リソース」の定義方法と、
  • 前述の各「マークアップ拡張」を使用し、
    プロパティ値に「リソース」への参照を指定する方法

について説明する。

※「リソース」の「データ バインディング」については、
リソースとのデータ バインディングを参照のこと。

リソースの定義

「リソース」とは、ResourceDictionary? 型のオブジェクトであり、

  • ApplicationオブジェクトのResources プロパティ
  • 各UI要素(FrameworkElement? or FrameworkContentElement?)のResources プロパティ

に「プロパティ要素構文」を使用して定義できる。

このため、任意のUI要素に定義可能であるが、
通常はルート要素(Window or Page)上に定義する。

また、「リソース」は、定義場所により以下のような呼称・特徴がある。

リソースの定義場所

  • アプリケーション リソース
    ApplicationオブジェクトのResourcesプロパティに定義された「リソース」を
    「アプリケーション リソース」と呼び、全体から(つまりどこからでも)参照できるという特徴を持っている。
  • ウィンドウ ページ リソース
    Window・PageオブジェクトのResourcesプロパティに定義された「リソース」を
    「アプリケーション リソース」と呼び、そのWindow・Pageと、その子要素から参照できるという特徴を持っている。
  • イミディエイト リソース
    各UI要素(FrameworkElement? or FrameworkContentElement?)の
    Resourcesプロパティに定義された「リソース」を「イミディエイト・リソース」と呼び、
    「リソース」を定義した要素と、その子要素から参照できるという特徴を持っている。

リソースの定義例

  • 「リソース」には、前述のx:Key ディレクティブを使用して一意のキーを持たせて定義する必要がある。
    <Window.Resources>
      <x:Array x:Key="List" Type="{x:Type sys:String}">
        <sys:String>A</sys:String>
        <sys:String>B</sys:String>
        <sys:String>C</sys:String>
      </x:Array>
    </Window.Resources>

    ※ 先頭で、String型のインポートが必要
    xmlns:sys="clr-namespace:System;assembly=mscorlib"

  • 補足:ジェネリック コレクションのリソース
    ジェネリック コレクションを「リソース」に定義するには、x:TypeArguments?のディレクティブを使用して、
    ObservableCollection?<T>コレクション クラスなどのジェネリック型のコンストラクタに渡す必要があるが、
    これは、XAML2009 からのサポートとなる。

リソースの使用方法

「リソース」を定義したら、各種「マークアップ拡張」を使用して、
プロパティ値に「データ バインディング」や、「リソース」への参照を指定できる。

この時、参照する側からx:Key ディレクティブを使用して割り当てたキーを「リソース」検索のキーとして指定できる。

なお、「スタイル」や「テンプレート」などは、キーを定義せず、TargetType? 属性のみ使用して、
指定の型のオブジェクトに「スタイル」や「テンプレート」を適用することもできる。
これについては、「スタイルとテンプレート」を参照のこと。

リソースの定義と参照

以下、「リソース」の定義と、プロパティ値に「リソース」への参照を指定する例を示す。

StaticResource?参照の例

StaticResource?参照では、

  • コンパイル時に「リソース」検索が行われ、各「リソース」に指定されたキーが存在するかどうかが確認される。
  • 「リソース」検索により、「リソース」が発見できなかった場合は、コンパイル時にエラーとなる。

以下は、StaticResource?の定義と参照例。

  • XAML
    • StaticResource?の定義と参照例(1)
      • 以下は、「マークアップ拡張」を使用して、「プロパティ属性構文」でStaticResource?参照した例である。
      • また、属性名を省略すると自動的にコンストラクタに渡される動作を利用して、
        ResourceKey?属性に指定の値を渡す旨を指示する「ResourceKey?=」という記述を省略することができる。
<Window.Resources>
  <SolidColorBrush x:Key="BlueBrush" Color="Blue"/>
</Window.Resources>
<Grid>
  <Ellipse Fill="{StaticResource ResourceKey=BlueBrush}" Height="150" Width="150"/>
</Grid>

↓ 「ResourceKey? =」という記述を省略

<Window.Resources>
  <SolidColorBrush x:Key="BlueBrush" Color="Blue"/>
</Window.Resources>
<Grid>
  <Ellipse Fill="{StaticResource BlueBrush}" Height="150" Width="150"/>
</Grid>
  • StaticResource?の定義と参照例(2)
    以下は、一般的ではないが「プロパティ要素構文」を使用してStaticResource?参照した例である。
    なお、WPFのみ、StaticResource?参照を「プロパティ要素構文」で記述可能である。
    <Window.Resources>
      <SolidColorBrush x:Key="BlueBrush" Color="Blue"/>
    </Window.Resources>
    <Grid>
      <Ellipse Height="150" Width="150">
        <Ellipse.Fill>
          <StaticResource ResourceKey="BlueBrush"/> 
        </Ellipse.Fill>
      </Ellipse>
    </Grid>
  • レンダリング結果
    StaticResourceの定義と参照例(1)

DynamicResource?参照の例

  • DynamicResource?参照では、アプリケーションの起動時に、
    後で「リソース」検索を行う際に使用する一時的な式が作成され、
    アプリケーションの実行後、「リソース」が必要になった際に都度、「リソース」検索が行われる。
  • このためDynamicResource?参照は、
    • 主に「リソース」の値が変更される場合、StaticResource?参照に代替して使用する。
    • 「リソース」検索により、「リソース」が発見できなかった場合は、デフォルト値が設定される。
  • なお、DynamicResource?参照は、WPFでのみ利用可能で、「Silverlight」では利用不可である。

以下は、DynamicResource?の定義と参照例。

  • 以下は、リソースがアプリケーション外部から変更される場合の例
    以下は、デスクトップ画面のテーマの変更(Windows XP → Windowsクラシック)を反映する例である。
<Grid>
  <Grid.RowDefinitions>
    <RowDefinition/>
    <RowDefinition/>
  </Grid.RowDefinitions>
  <Ellipse Grid.Row="0"
    Fill="{StaticResource ResourceKey={x:Static SystemColors.HighlightBrushKey}}"/>
  <Ellipse Grid.Row="1"
    Fill="{DynamicResource ResourceKey={x:Static SystemColors.HighlightBrushKey}}"/>
</Grid>
DynamicResourceの定義と参照例(リソースがアプリケーション外部から変更される場合)
  • 以下は、リソースがアプリケーション内部から変更される場合の例
    以下は、アプリケーションからの「リソース」の動的な変更を反映する例である。
    なお、「リソース」の動的な変更は、FrameworkElement?.Resourcesプロパティで変更できる。
    • XAML
      <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="Blue"/>
      </Window.Resources>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition/>
          <RowDefinition/>
          <RowDefinition Height="23"/>
        </Grid.RowDefinitions>
        <Ellipse Grid.Row="0" Fill="{StaticResource MyBrush}"/>
        <Ellipse Grid.Row="1" Fill="{DynamicResource MyBrush}"/>
        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
          <Button Content="青" Height="23" Name="button1" Width="75" Click="button1_Click" />
          <Button Content="赤" Height="23" Name="button2" Width="75" Click="button2_Click" />
        </StackPanel>
      </Grid>
  • コード ビハインド
    private void button1_Click(object sender, RoutedEventArgs e) {
      this.Resources["MyBrush"] = Brushes.Blue;
    }
    private void button2_Click(object sender, RoutedEventArgs e) {
      this.Resources["MyBrush"] = Brushes.Red;
    }
DynamicResourceの定義と参照例(リソースがアプリケーション内部から変更される場合

StaticResource?参照 + ディクショナリ ファイルの例

ResourceDictionary?は、「ディクショナリ ファイル」に定義することもできる。

「ディクショナリ ファイル」を追加する際は、

  1. プロジェクト ファイルを右クリックし、
  2. [追加] → [リソース ディクショナリ]を選択するか、
    [追加] → [新しい項目]を選択し、表示されるテンプレートから[リソース ディクショナリ(WPF)]を選択する。
ディクショナリ ファイルの追加
  • 前述のStaticResource?参照の例を、
    「ディクショナリ ファイル」を使用するように書き直した例を以下に示す。
    <ResourceDictionary
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <SolidColorBrush x:Key="BlueBrush" Color="Blue"/>
    </ResourceDictionary>
    ディクショナリ ファイルの定義例 (Dictionary1.xaml)
  • 「ディクショナリ ファイル」を「イミディエイト・リソース」にロードする。
    <Window.Resources>
      <ResourceDictionary Source="Dictionary1.xaml"/>
    </Window.Resources>
    <Grid>
      <Ellipse Fill="{StaticResource BlueBrush}" Height="150" Width="150"/>
    </Grid>
StaticResource参照 + ディクショナリ ファイルの使用例

StaticResource?参照 + ディクショナリ ファイルの使用例

DynamicResource?参照 + ディクショナリ ファイルの例

  • DynamicResource?参照とResourceDictionary?を組み合わせ、
  • ResourceDictionary?を、Application.LoadComponent?で動的にロード、
  • Application.Current.Resourcesに設定することにより、
  • スタイル」(スキン )などの設定をアプリケーションから動的に変更することが可能である。

以下の、その例を示す。

  • Dictionary
    • Dictionary1.xaml
      <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <SolidColorBrush x:Key="MyBrush" Color="Blue"/>
      </ResourceDictionary>
    • Dictionary2.xaml
      <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <SolidColorBrush x:Key="MyBrush" Color="Red"/>
      </ResourceDictionary>
  • XAML
    <StackPanel>
      <Ellipse Fill="{DynamicResource MyBrush}" Height="150" Width="150"/>
      <Button Content="青" Height="23" Name="button1" Width="75" Click="button1_Click" />
      <Button Content="赤" Height="23" Name="button2" Width="75" Click="button2_Click" />
    </StackPanel>
  • コード ビハインド
    public partial class Window1 : Window {
      public Window1() {
        InitializeComponent();
        this.LoadResourceDictionary("Dictionary1.xaml");
      }
    
      private void button1_Click(object sender, RoutedEventArgs e) {
        this.LoadResourceDictionary("Dictionary1.xaml");
      }
    
      private void button2_Click(object sender, RoutedEventArgs e) {
        this.LoadResourceDictionary("Dictionary2.xaml");
      }
    
      private void LoadResourceDictionary(string name) {
        ResourceDictionary dictionary =
          (ResourceDictionary)Application.LoadComponent(new Uri(name, UriKind.Relative));
        if (dictionary != null) Application.Current.Resources = dictionary;
      }
    }
DynamicResource参照 + ディクショナリ ファイルの使用例

DynamicResource?参照 + ディクショナリ ファイルの使用例

データ バインディング

様々なソースと「データ バインディング」するサンプルを示す。

なお、サンプルのXAMLソースを読むためには、以下の、
Bindingプロパティの設定方法を理解しておく必要がある。

Bindingプロパティの設定方法

項番プロパティ説明
ModeOneTime?OneWay?TwoWay? のいずれかの「バインディング モード」を指定する。
Source「バインディング ソース」を指定する。「バインディング ソース」は、
リソース」に定義し、StaticResourceの「マークアップ拡張」を使用して「データ バインディング」するか、
FrameworkElement?.DataContext?から、「データ バインディング」する。
ElementName?UI要素名を使用して「バインディング ソース」を指定する。
RelativeSource「バインディング ターゲット」からの相対位置で「バインディング ソース」を指定する。
※ Self、TemplatedParent?AncestorType?AncestorLevel?属性などを使用して指定する。
Path「バインディング ソース」のプロパティ名を指定する(名前空間のフルパスで指定可)。
なお、インデクサなどの指定も可能である。詳細は、以下のURLを参照のこと。
XPath「バインディング ソース」として、XMLデータソースのXML DOMへのパスを表す文字列を指定する。
TargetNullValue?ソース値が null のときに返される値を指定する。
Converter値コンバータ」を指定する。
値コンバータ」は、インスタンス化が可能であり、ResourceDictionary?に配置できる。
値コンバータ」は「StaticResource?」に定義しStaticResourceの「マークアップ拡張」を使用して参照するようにする。
ConverterCulture?値コンバータ」で使用するカルチャを指定する。
10ConverterParameter?値コンバータ」の変換ロジックで使用するパラメタを指定する。

データ バインディングの基礎

以下、「WPFのアーキテクチャ - データ バインディング」に対応するサンプルを示す。

コード ビハインドからのデータ バインディング(モード:OneWay?

  • モード:OneWay?の「データ バインディング」を行う。
  • 「データ バインディング」の基本的な動作を理解するために、
    • Bindingオブジェクトをコード ビハインドで自作し、
    • 以下の2つのオブジェクトを結びつける。
      • 「バインディング ソース」
      • 「バインディング ターゲット」
  • XAML
    <StackPanel 
      x:Name="MainPanel"
      Loaded="MainPanel_Loaded">
      <TextBlock x:Name="TextBlock1"/>
      <TextBlock x:Name="TextBlock2"/>
    </StackPanel>
  • コード ビハインド
    public partial class Window1 : Window {
      public Window1() {
        InitializeComponent();
      }
    
      private void MainPanel_Loaded(object sender, RoutedEventArgs e)
      {
        // バインディング ソース(Person)
        Person BindingSource =
          new Person() { Height = 1.7, Weight = 60 };
    
        // Bindingオブジェクトによりバインド
    
        // HeightプロパティをOneWayでバインド
        Binding BindingHeight = new Binding("Height");
        BindingHeight.Mode = BindingMode.OneWay;
        // バインディング ソース(Person)
        BindingHeight.Source = BindingSource;
        // バインディング ターゲット(TextBlock)
        TextBlock1.SetBinding(TextBlock.TextProperty, BindingHeight);
    
        // WeightプロパティをOneWayでバインド
        Binding BindingWeight = new Binding("Weight");
        BindingWeight.Mode = BindingMode.OneWay;
        // バインディング ソース(Person)
        BindingWeight.Source = BindingSource;
        // バインディング ターゲット(TextBlock)
        TextBlock2.SetBinding(TextBlock.TextProperty, BindingWeight);
      } 
    }
    
    /// <summary>バインディング ソース(Person)</summary>
    public class Person {
      public double Height { get; set; }
      public double Weight { get; set; }
    }

コード ビハインドからのDataContext?を使用したデータ バインディング(モード:OneWay?

上記のMainPanel_Loadedメソッドのコードを一部編集し、MainPanel?StackPanel?)の
FrameworkElement?.DataContext?プロパティに「バインディング ソース」を設定することで、
Bindingオブジェクトへの「バインディング ソース」の指定が不要になる。

  • コード ビハインド
    private void MainPanel_Loaded(object sender, RoutedEventArgs e) {
      // バインディング ソース(Person)
      MainPanel.DataContext =
        new Person() { Height = 1.7, Weight = 60 };
    
      // Bindingオブジェクトによりバインド
    
      // HeightプロパティをOneWayでバインド
      Binding BindingHeight = new Binding("Height");
      BindingHeight.Mode = BindingMode.OneWay;
      // バインディング ターゲット(TextBlock)
      this.TextBlock1.SetBinding(TextBlock.TextProperty, BindingHeight);
    
      // WeightプロパティをOneWayでバインド
      Binding BindingWeight = new Binding("Weight");
      BindingWeight.Mode = BindingMode.OneWay;
      // バインディング ターゲット(TextBlock)
      TextBlock2.SetBinding(TextBlock.TextProperty, BindingWeight);
    }

BindingオブジェクトをXAMLで実装する(プロパティ要素構文

  • BindingオブジェクトをXAMLのマークアップ機能で実装する。
    更に、XAMLのマークアップ機能を使用して、Bindingオブジェクトの生成処理・設定処理をXAML側に移動することで、
    上記のXAMLとMainPanel_Loadedメソッドのコードを一部編集し、コード ビハインドのコード量を削減できる。
  • XAML
    <StackPanel 
      x:Name="MainPanel"
      Loaded="MainPanel_Loaded">
      <TextBlock>
        <TextBlock.Text>
          <Binding Path="Height" Mode="OneWay"/>
        </TextBlock.Text>
      </TextBlock>
      <TextBlock>
        <TextBlock.Text>
          <Binding Path="Weight" Mode="OneWay"/>
        </TextBlock.Text>
      </TextBlock>
    </StackPanel>
  • コード ビハインド
    private void MainPanel_Loaded
       (object sender, RoutedEventArgs e) {
    
      // バインディング ソース(Person)
      MainPanel.DataContext =
      new Person() { Height = 1.7, Weight = 60 };
    }

Bindingオブジェクトをバインディングのマークアップ拡張で実装する(プロパティ属性構文

  • 一般的には、「データ バインディング」を実装する場合、XAML側の記述は(前述とは異なる)
    「バインディングのマークアップ拡張」により「プロパティ属性構文」化して実装する方法を採る。
  • 上記のXAMLを「バインディングのマークアップ拡張」を使用して、「プロパティ属性構文」化して実装すると次のようになる。
    <StackPanel x:Name="MainPanel" Loaded="MainPanel_Loaded">
      <TextBlock Text="{Binding Path=Height, Mode=OneWay}" />
      <TextBlock Text="{Binding Path=Weight, Mode=OneWay}" />
    </StackPanel>
  • また、属性名を省略すると自動的にコンストラクタに渡される動作を利用して、
    (Path属性に指定の値を渡す旨を指示する)「Path=」という記述を省略することができる。
    <StackPanel x:Name="MainPanel" Loaded="MainPanel_Loaded">
      <TextBlock Text="{Binding Height, Mode=OneWay}" />
      <TextBlock Text="{Binding Weight, Mode=OneWay}" />
    </StackPanel>

変更通知の追加(モード:OneWay? or OneWayToSource?

下記は、モード:OneWay? or OneWayToSource?を併用した「データ バインディング」によるBMI(肥満度)の自動計算アプリケーションの例である。
この例では、「バインディング ソース」のプロパティ値の変更を、自動的に「バインディング ターゲット」に反映できる。
なお、この際、「バインディング ソース」は、INotifyPropertyChanged?インターフェイスを実装して、BMI(肥満度)プロパティの変更通知処理を実装する必要がある。

  • XAML
    <StackPanel x:Name="MainPanel" Loaded="MainPanel_Loaded">
      <TextBox Margin="10" Text="{Binding Height, Mode=OneWayToSource}"/>
      <TextBox Margin="10" Text="{Binding Weight, Mode=OneWayToSource}"/>
      <Button Margin="10" Content="計算" Click="Button_Click" />
      <TextBlock Margin="10" Text="{Binding Bmi, Mode=OneWay}"/>
    </StackPanel>
  • コード ビハインド
    /// <summary>/// Window1.xaml の相互作用ロジック/// </summary>
    public partial class Window1 : Window {
      public Window1() {
        InitializeComponent();
      }
    
      /// <summary>初期化イベント</summary>
      private void MainPanel_Loaded(object sender, RoutedEventArgs e) {
        // バインド ソース(Person)
        MainPanel.DataContext = new Person();
      }
      /// <summary>計算イベント</summary>
      private void Button_Click(object sender, RoutedEventArgs e) {
        ((Person)MainPanel.DataContext).Calculate();
      } 
    }
  • バインディングソース
    /// <summary>バインド ソース(Person)</summary>
    public class Person : INotifyPropertyChanged {
      public double Height { get; set; }
      public double Weight { get; set; }
    
      double _bmi;
      public double Bmi {
        get { return this._bmi; }
        set {
          this._bmi = value;
    
          // 変更通知
          this.OnPropertyChanged("Bmi");
        }
      }
    
      /// <summary>計算処理</summary>
      public void Calculate() {
        this.Bmi = Weight / Math.Pow(Height, 2);
      }
    
      #region INotifyPropertyChanged メンバ
    
      /// <summary>値変更イベントの定義</summary>
      public event PropertyChangedEventHandler PropertyChanged;
    
      /// <summary>変更通知(値変更イベントを発生させる)</summary>
      protected virtual void OnPropertyChanged(string propertyName) {
        PropertyChangedEventHandler handler = this.PropertyChanged;
    
        if (handler != null)
          handler(this, new PropertyChangedEventArgs(propertyName));
      }
    
      #endregion
    }

値コンバータを使用したデータ バインディング(モード:OneWay? or OneWayToSource?

続いて、上記処理に、BMI(肥満度)プロパティが

  • 18.5未満の場合、出力先のTextBlock?の背景色を青色に
  • 25以上の場合、出力先のTextBlock?の背景色を赤色に

変更する処理を追加する。

これには別途、背景色プロパティを定義することで実現することも可能であるが、
ここでは、前述のIValueConverter?インターフェイスを実装した「値コンバータ」を実装することでこれを実現する。
値の変換処理は、「値コンバータ」のConvertメソッドに実装する。

  • XAML
    <Window.Resources>
      <my:BmiLevelConverter x:Key="bmiLevelConverter"/>
    </Window.Resources>
    <StackPanel x:Name="MainPanel" Loaded="MainPanel_Loaded">
      <TextBox Margin="10" Text="{Binding Height, Mode=OneWayToSource}"/>
      <TextBox Margin="10" Text="{Binding Weight, Mode=OneWayToSource}"/>
      <Button Margin="10" Content="計算" Click="Button_Click" />
      <TextBlock Margin="10"
        Text="{Binding Bmi, Mode=OneWay}"
        Background="{Binding Bmi, Mode=OneWay,
        Converter={StaticResource bmiLevelConverter}}"/>
    </StackPanel>
  • 値コンバータ
    /// <summary>BMIレベルに合った背景色を返す</summary>
    public class BmiLevelConverter : IValueConverter {
      #region IValueConverter メンバ
    
      /// <summary>変換メソッド(ソースからターゲット)</summary>
      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        // BMIレベルに合った背景色を返す
        double target = (double)value;
        SolidColorBrush level;
    
        if (target < 18.5)
          level = new SolidColorBrush(Colors.Blue);
        else if (target > 25)
          level = new SolidColorBrush(Colors.Red);
        else
          level = new SolidColorBrush(Colors.White);
    
        return level;
      }
    
      /// <summary>変換メソッド(ターゲットからソース)</summary>
      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
        throw new NotImplementedException();
      }
    
      #endregion
    }

INotifyPropertyChanged?の変更通知を「依存関係プロパティ」で代替

なお、変更通知は、INotifyPropertyChanged?インターフェイスの実装ではなく、「依存関係プロパティ」でも代替できる。
上記の例のBMI(肥満度)プロパティを「依存関係プロパティ」として、以下のように書き直し、動作を確認する。

  • バインディングソース
    /// <summary>バインド ソース(Person)</summary>
    public class Person : DependencyObject {
      public double Height { get; set; }
      public double Weight { get; set; }
    
      /// <summary>Bmiプロパティを依存関係プロパティとして登録</summary>
      public static readonly DependencyProperty BmiProperty =
        DependencyProperty.Register("Bmi", typeof(double), typeof(Person), 
            new UIPropertyMetadata(0.0));
    
      /// <summary>BmiプロパティのCLRプロパティ</summary>
      public double Bmi {
        get { return (double)this.GetValue(Person.BmiProperty); }
        set { this.SetValue(Person.BmiProperty, value); }
      }
    
      /// <summary>計算処理</summary>
      public void Calculate(){
        this.Bmi = Weight / Math.Pow(Height, 2);
      }
    }

BMI(肥満度)プロパティが変更されると、UI要素(TextBlock?)に変更が反映されることを確認できる。
このように、「依存関係プロパティ」は既定で変更通知をサポートしている。

双方向のデータ バインディング(モード:TwoWay?

以下の例では、モード:TwoWay?の「データ バインディング」を行うため、
2つのUI要素、TextBox?とSliderを双方向接続した例である。

なお前述のようにUI要素の表示に関するプロパティは、
基本的に「依存関係プロパティ」として定義されており、
既定で変更通知をサポートしている。このため、既定で双方向接続が可能である。

なお、「バインディング ソース」をUI要素に接続する場合は、ElementName?属性を使用すると良い。

  • TextBox?(ソース)→Slider(ターゲット)
  • XAML
    <StackPanel Orientation="Vertical">
      <Label>TextBox</Label>
      <TextBox x:Name="textBox1" Text="5"/>
      <Rectangle Height="20"></Rectangle>
      <Label>Slider</Label>
      <Slider x:Name="Slider1" 
        Value="{Binding ElementName=textBox1, Path=Text, Mode=TwoWay}"/>
    </StackPanel>
  • Slider(ソース)→TextBox?(ターゲット)
    • バインディング ソース、バインディング ターゲットを反転する。
    • すると「バインディング ターゲット」(TextBox?)の変更が直ちに通知されなくなる。
      TextBox?から、フォーカスが外れないとSliderに変更値が反映されない)。
    • これは、ほとんどの依存関係プロパティの既定値は PropertyChanged?であるが、Textプロパティの既定値は LostFocus?のため。
    • この場合、「バインディングのマークアップ拡張」に、「UpdateSourceTrigger?=PropertyChanged?」 という属性の記述を追加することで、
      「バインディング ターゲット」(TextBox?)からの変更が直ちに「バインディング ソース」(Slider)に通知されるようになる。
  • XAML
    <StackPanel Orientation="Vertical">
      <Label>TextBox</Label>
      <TextBox x:Name="textBox1"
        Text="{Binding ElementName=Slider1, Path=Value, Mode=TwoWay}"/>
      <Rectangle Height="20"></Rectangle>
      <Label>Slider</Label>
      <Slider x:Name="Slider1" Value="5"/>
    </StackPanel>

値コンバータを使用したデータ バインディング(モード:TwoWay?

双方向の値の変換に対応した「値コンバータ」は、
Convert、ConvertBack?メソッドの双方を実装する必要がある。

  • XAML
    <Window.Resources>
      <my:ValConverter x:Key="valConverter" />
    </Window.Resources>
    <StackPanel Orientation="Vertical">
      <Label>TextBox</Label>
      <TextBox x:Name="textBox1" Text="500"/>
      <Rectangle Height="20"></Rectangle>
      <Label>Slider</Label>
      <Slider x:Name="Slider1"
        Value="{Binding ElementName=textBox1, Path=Text, Mode=TwoWay,
        Converter={StaticResource valConverter}}"/>
    </StackPanel>
  • コード ビハインド
    public class ValConverter : IValueConverter {
      #region IValueConverter メンバ
    
      /// <summary>変換メソッド(ソースからターゲット)</summary>
      public object Convert(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture) {
        return int.Parse(value.ToString()) / 100;
      }
    
      /// <summary>変換メソッド(ターゲットからソース)</summary>
      public object ConvertBack(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture) {
        return ((double)value) * 100;
      }
    
      #endregion
    }

ItemsSource?へのデータ バインディング

ItemsControl?から派生した要素のItemsSource?属性にコレクションを「データ バインディング」する場合、対象オブジェクトは反復処理をサポートしている必要がある。

  • ItemsSource?へのデータ バインディング(UI要素を含まない「データ バインディング」)
    以下は、ObservableCollection?<T> コレクション クラスを使用して「データ バインディング」する例である。
    なお、ObservableCollection?<T>コレクション クラスは、INotifyPropertyChanged?インターフェイスを継承しており、変更通知が可能となっている。
  • XAML
    <StackPanel x:Name="stackPanel1">
      <ListBox x:Name="listBox1" ItemsSource="{Binding}"/>
      <Button Height="23" Name="button1" Click="button1_Click">Button</Button>
    </StackPanel>
  • コード ビハインド
    public partial class Window1 : Window {
    
      ObservableCollection<string> _cities = new ObservableCollection<string>();
    
      public Window1() {
        InitializeComponent();
    
        this._cities.Add("札幌市");
        this._cities.Add("仙台市");
        this._cities.Add("静岡市");
    
        this.stackPanel1.DataContext = _cities;
      }
    
      private void button1_Click(object sender, RoutedEventArgs e) {
        this._cities.Add("神戸市" + this.listBox1.Items.Count.ToString());
      }
    }
  • UI要素を含まない「データ バインディング」の実装例について、
    • 上記例では、button1_ClickイベントでItemがObservableCollection?<T>コレクションに追加さると直ちにListBox?の表示に反映されることが確認できる。
      また、この変更通知をサポートしているObservableCollection?<T>コレクション クラスを、変更通知をサポートしないCollection<T> コレクション クラスに変更した場合、
      Collection<T>コレクションに追加されたItemがListBox?の表示に反映されないことも確認できる。
    • なお、変更通知に対応したカスタム コレクションを作成する場合は、
      ObservableCollection?<T>や、Collection<T> & INotifyCollectionChanged?を継承したカスタム コレクションを作成すると良い。
    • この際の注意点としては、後者のCollection<T> & INotifyCollectionChanged?を継承したカスタム コレクションを作成する場合、
      Add、RemoveAt?などのメソッドに変更通知を実装し、「変更通知の基盤処理」の値変更イベントに、
      前述のPropertyChangedEventHandler?ではなく、NotifyCollectionChangedEventHandler?を使用する点である。
  • ItemsSource?へのデータ バインディング(UI要素を含んだ「データ バインディング」)
    以下は、「Itemsプロパティ」の例を「データ バインディング」で実装した例である。
  • XAML
    <StackPanel x:Name="stackPanel1">
      <ListBox ItemsSource="{Binding}"/>
    </StackPanel>
  • コード ビハインド
    public Window1() {
      InitializeComponent();
    
      // バンディング ソース(コレクション)
      ObservableCollection<Border> cities = new ObservableCollection<Border>();
    
      // ワーク
      Border tmpBorder = null;
      StackPanel tmpStackPanel = null;
      Image tmpImage = null;
      TextBlock tmpTextBlock = null;
    
      // Borderの構築
      tmpBorder = new Border();
      tmpBorder.BorderBrush = Brushes.Black;
      tmpBorder.BorderThickness = new Thickness(1);
      tmpBorder.Margin = new Thickness(5);
    
      // StackPanelの構築
      tmpStackPanel = new StackPanel();
      tmpStackPanel.Orientation = Orientation.Horizontal;
      tmpStackPanel.Height = 120;
      tmpStackPanel.Width = 250;
      tmpStackPanel.Margin = new Thickness(5);
    
      // Imageの構築
      tmpImage = new Image();
      tmpImage.Source=new BitmapImage(
        new Uri(@".\Water lilies.jpg", UriKind.Relative));
      tmpImage.Height=100;
    
      // TextBlockの構築
      tmpTextBlock = new TextBlock();
      tmpTextBlock.Text="Water lilies";
      tmpTextBlock.VerticalAlignment = VerticalAlignment.Center;
    
      // 階層構造の組み立て
      tmpStackPanel.Children.Add(tmpImage);
      tmpStackPanel.Children.Add(tmpTextBlock);
    
      tmpBorder.Child = tmpStackPanel;
    
      // バンディング ソース(コレクション)に追加
      cities.Add(tmpBorder);
    
      // ・・・(n項繰り返し)・・・
    
      // データ バンディング
      stackPanel1.DataContext = cities;
    }
  • UI要素を含んだ「データ バインディング」の実装例について、

インデクサによるデータ バインディング

インデクサについても、Path属性に角括弧を指定することで接続可能である。

  • XAML
    <Grid>
      <Image x:Name="Image1"
        Height="200" Width="200"
        Source="{Binding Path=[Source]}"
        ToolTip="{Binding Path=[ToolTip]}"/>
    </Grid>
  • コード ビハインド
    public Window1() {
      InitializeComponent();
    
      Hashtable ht = new Hashtable();
      ht["Source"] = @".\Winter.jpg";
      ht["ToolTip"] = "Winter";
      this.Image1.DataContext = ht;
    }

これらを応用すると、

インデクサを持つオブジェクトの配列(反復処理をサポート)
をデータ バインディングすることも可能であることが分かる。

ビジネス・アプリケーションでは、DataGrid?DataTable?をデータ バインディングする際に、
DataGrid?の列へDataTable?の列をマッピングするようなケースでよく利用します。

リソースとのデータ バインディング

リソース」参照を「バインディングのマークアップ拡張」でも使用することができる。

StaticResource?を使用したデータ バインディング

  • 例えば、静的な「リソース」を使用し、以下のように「データ バインディング」できる。
  • StaticResource?のデータ バインディング (1)
    <Window.Resources>
      <sys:String x:Key="val">Click Here</sys:String >
    </Window.Resources>
    <StackPanel>
      <Button Content="{Binding Source={StaticResource val}}"/>
    </StackPanel>

    ※ 先頭で、String型のインポートが必要
    xmlns:sys="clr-namespace:System;assembly=mscorlib"

  • ItemsControl?から派生したUIコントロールのItemsSource?属性に「データ バインディング」する場合、
    リソース」の対象オブジェクトは反復処理をサポートしている必要がある。
    • StaticResource?のデータ バインディング (2)
      <Window.Resources>
        <x:Array x:Key="list"
          Type="{x:Type sys:String}">
          <sys:String>A</sys:String>
          <sys:String>B</sys:String>
          <sys:String>C</sys:String>
        </x:Array>
      </Window.Resources>
      <StackPanel>
        <ListBox ItemsSource="{Binding Source={StaticResource list}}"/>
      </StackPanel>

      ※ 先頭で、String型のインポートが必要
      xmlns:sys="clr-namespace:System;assembly=mscorlib"

DynamicResource?を使用したデータ バインディング

動的な「リソース」を使用した「データ バインディング」はサポートされない。
以下、「{Binding Source={DynamicResource?」をテストした際のエラーメッセージである。

型 'Binding' の 'Source' プロパティで 'DynamicResourceExtension' を設定することはできません。'DynamicResourceExtension' は、DependencyObject の DependencyProperty でのみ設定できます。

レイアウト

ココでは、レイアウト方法として、

の2つについて説明する。

レイアウトのプロパティ

  • ココでは、「レイアウト」関係のプロパティについて説明する。
  • これらのプロパティは、「レイアウト」機能を追加するFrameworkElement?基本クラスにより提供される。
    • 配置の際、各要素間に余白を指定したい場合は、Margin、Padding属性を使用する。
      • Margin :自要素と親要素の間
      • Padding:自要素と子要素の間
        ※ [left, top, right, bottom]と設定可能。省略時の動作は、リファレンスを参照のこと。
    • 配置の際、「揃え」を指定したい場合は、HorizontalAlignment?VerticalAlignment?属性を使用する。
      • HorizontalAlignment? :水平方法の揃え(Center、Left、Right、Stretchを指定可)
      • VerticalAlignment? :垂直方法の揃え(Center、Top、Bottom、Stretchを指定可)
        ※ Stretchは、引き伸ばして余白を埋めて配置の意味。
  • 以下、「レイアウト」関係のプロパティを使用したXAMLとレンダリングの例を示す。
    • XAML
      <Border Background="LightBlue" BorderBrush="Black" BorderThickness="2" Padding="15">
        <StackPanel Background="White" HorizontalAlignment="Center" VerticalAlignment="Top">
          <TextBlock Margin="5,0,5,0" HorizontalAlignment="Center">Alignment, Margin and Padding Sample</TextBlock>
          <Button HorizontalAlignment="Left" Margin="20">Button 1</Button>
          <Button HorizontalAlignment="Right" Margin="10">Button 2</Button>
          <Button HorizontalAlignment="Stretch" Margin="0">Button 3</Button>
        </StackPanel>
      </Border>
    • レンダリング

パネルの種類と使い方

WPFでは、パネルを使用した「レイアウト」が可能である。

  • パネルには、以下のような特徴がある。
    • 複数のUIElementを含めることができる(UIElementCollection? のプロパティを持つ)。
  • 自分に含まれるUIElementを管理する。
  • パネル自体もUIElementであるため、パネルへの組み込みが可能
  • 使用可能なパネル要素には次のものがある。
    #パネル名クラス名説明
    1キャンバス パネルCanvas座標を使用して子要素を明示的に配置できる領域を定義する。
    2ドック パネルDockPanel?パネルの各「辺」に、子要素をドッキングする領域を定義する。
    3スタック パネルStackPanel?子要素を水平方向または垂直方向に整列する領域を定義する。
    4折り返しパネルWrapPanel?子要素を水平方向または垂直方向に整列し、端に達したら改行して整列する領域を定義する。
    5(均一)グリッド パネルUniformGrid?列と行で構成されているグリッド領域を定義する。
    6グリッド パネルGrid列と行で構成されている柔軟なグリッド領域を定義する。
  • また、親要素であるパネルへ配置する子要素の座標などの情報については、
    子要素から「添付プロパティ」を使用して設定可能である。

キャンバス パネル

  • 座標を使用して子要素を明示的に配置できる領域を定義する。
    • XAML
      <Canvas Background="LightSteelBlue">
        <TextBlock Canvas.Top="10" Canvas.Left="20">
          Hello World!
        </TextBlock>
        <TextBlock Canvas.Top="40" Canvas.Left="50">
          絶対位置決め方式は便利ではありませんか?
        </TextBlock>
      </Canvas>
    • レンダリング結果
      キャンバス パネル
  • キャンバス パネルでは、「添付プロパティ」であるCanvas.Top・Canvas.Left属性を使用して、
    子要素から座標位置を明示的に指定・配置できるため、Windowsフォームに近い開発が可能である。

ドック パネル

  • パネルの各「辺」に、子要素をドッキングする領域を定義する。
    • XAML
      <DockPanel LastChildFill="True">
        <Border Height="25" Background="SkyBlue" DockPanel.Dock="Top">
          <TextBlock Foreground="Black">Dock = "Top(1)"</TextBlock>
        </Border>
        <Border Height="25" Background="SkyBlue" DockPanel.Dock="Top">
          <TextBlock Foreground="Black">Dock = "Top(2)"</TextBlock>
        </Border>
        <Border Height="25" Background="LemonChiffon" DockPanel.Dock="Bottom">
          <TextBlock Foreground="Black">Dock = "Bottom"</TextBlock>
        </Border>
        <Border Width="100" Background="PaleGreen" DockPanel.Dock="Left">
          <TextBlock Foreground="Black">Dock = "Left"</TextBlock>
        </Border>
        <Border Background="White">
          <TextBlock Foreground="Black">
            This content will "Fill" the remaining space
          </TextBlock>
        </Border>
      </DockPanel>
    • レンダリング結果
      ドック パネル
  • DockPanel?.LastChildFill?プロパティ(LastChildFill?属性)をtrue (既定値)に設定すると、
    DockPanel?の最終の子要素は常に残りの領域を埋めるようになる。

スタック パネル

  • 子要素を水平方向または垂直方向に整列する領域を定義する。
    • XAML
      • 水平方向
        <StackPanel Orientation="Horizontal">
          <RadioButton Margin="4">test1</RadioButton>
          <RadioButton Margin="4">test2</RadioButton>
          <RadioButton Margin="4">test3</RadioButton>
        </StackPanel>
      • 垂直方向
        <StackPanel Orientation="Vertical">
          <RadioButton Margin="4">test1</RadioButton>
          <RadioButton Margin="4">test2</RadioButton>
          <RadioButton Margin="4">test3</RadioButton>
        </StackPanel>
    • レンダリング結果
      • 水平方向
        水平のスタック パネル
      • 垂直方向
        垂直のスタック パネル
  • 整列方向は、StackPanel?.Orientationプロパティ(Orientation属性)で指定する。

折り返しパネル

  • 子要素を水平方向または垂直方向に整列し、端に達したら改行して整列する領域を定義する。
    ※ パネルのサイズが変更されると、改行位置も変更される。
    • XAML
      • 水平方向
        <WrapPanel Orientation="Horizontal">            
          <Button Width="26" Height="26" Margin="4" Content="0"></Button>
          <Button Width="26" Height="26" Margin="4" Content="1"></Button>
          <Button Width="26" Height="26" Margin="4" Content="2"></Button>
         ・・・
          <Button Width="26" Height="26" Margin="4" Content="7"></Button>
          <Button Width="26" Height="26" Margin="4" Content="8"></Button>
          <Button Width="26" Height="26" Margin="4" Content="9"></Button>
        </WrapPanel>
      • 垂直方向
        <WrapPanel Orientation="Vertical">            
          <Button Width="26" Height="26" Margin="4" Content="0"></Button>
          <Button Width="26" Height="26" Margin="4" Content="1"></Button>
          <Button Width="26" Height="26" Margin="4" Content="2"></Button>
         ・・・
          <Button Width="26" Height="26" Margin="4" Content="7"></Button>
          <Button Width="26" Height="26" Margin="4" Content="8"></Button>
          <Button Width="26" Height="26" Margin="4" Content="9"></Button>
        </WrapPanel>
    • レンダリング結果
      • 水平方向
        水平の折り返しパネル
      • 垂直方向
        垂直の折り返しパネル
  • 整列方向は、WrapPanel?.Orientationプロパティ(Orientation属性)で指定する。

(均一)グリッド パネル

  • 列と行で構成されているグリッド領域を定義する。
    ※ グリッド内のすべてのセルが同じサイズである必要がある。
    • XAML
      • 3行 * 4列
        <UniformGrid Rows="3" Columns="4"> 
          <Button Width="26" Height="26" Margin="4" Content="0"></Button>
          <Button Width="26" Height="26" Margin="4" Content="1"></Button>
          <Button Width="26" Height="26" Margin="4" Content="2"></Button>
         ・・・
          <Button Width="26" Height="26" Margin="4" Content="7"></Button>
          <Button Width="26" Height="26" Margin="4" Content="8"></Button>
          <Button Width="26" Height="26" Margin="4" Content="9"></Button>
        </UniformGrid>
      • 4行 * 3列
        <UniformGrid Rows="4" Columns="3">
          <Button Width="26" Height="26" Margin="4" Content="0"></Button>
          <Button Width="26" Height="26" Margin="4" Content="1"></Button>
          <Button Width="26" Height="26" Margin="4" Content="2"></Button>
         ・・・
          <Button Width="26" Height="26" Margin="4" Content="7"></Button>
          <Button Width="26" Height="26" Margin="4" Content="8"></Button>
          <Button Width="26" Height="26" Margin="4" Content="9"></Button>
        </UniformGrid>
    • レンダリング結果
      • 3行 * 4列
        3行 * 4列の(均一)グリッド パネル
      • 4行 * 3列
        4行 * 3列の(均一)グリッド パネル
  • 行・列数は、UniformGrid?.Rows、UniformGrid?.Columns属性(UniformGrid?. Rows・Columnsプロパティ)で指定する。

グリッド パネル

  • 列と行で構成されている柔軟なグリッド領域を定義する。
    • 行・列の定義にGrid.RowDefinitions?、Grid.ColumnDefinitions?タグを使用する。
      • 「添付プロパティ」であるGrid.Row・Grid.Column属性を使用して子要素をセルへ配置する。
      • 列長・行長の設定も、この属性で指定する。
      • このためHTMLのテーブルと使い方は異なる。
  • XAML ( 4行 * 3列
    <Grid Width="250" Height="100">
      <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
        <ColumnDefinition />
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
      </Grid.RowDefinitions>
    
      <TextBlock Grid.ColumnSpan="3" Grid.Row="0">2005 Products Shipped</TextBlock>
      <TextBlock Grid.Row="1" Grid.Column="0">Quarter 1</TextBlock>
      <TextBlock Grid.Row="1" Grid.Column="1">Quarter 2</TextBlock>
      <TextBlock Grid.Row="1" Grid.Column="2">Quarter 3</TextBlock>
      <TextBlock Grid.Row="2" Grid.Column="0">50000</TextBlock>
      <TextBlock Grid.Row="2" Grid.Column="1">100000</TextBlock>
      <TextBlock Grid.Row="2" Grid.Column="2">150000</TextBlock>
      <TextBlock Grid.ColumnSpan="3" Grid.Row="3">Total Units: 300000</TextBlock>
    </Grid>
  • レンダリング結果
    4行 * 3列のグリッド パネル
  • なお、ColumnDefinition?タグのWidth属性に" n*"と指定することで、比率を指定できる。
    • XAML ( 比率を指定
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="1*"></ColumnDefinition>
          <ColumnDefinition Width="2*"></ColumnDefinition>
          <ColumnDefinition Width="3*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="1*"></RowDefinition>
          <RowDefinition Height="2*"></RowDefinition>
          <RowDefinition Height="3*"></RowDefinition>
        </Grid.RowDefinitions>
      </Grid>
    • レンダリング結果
      比率指定したグリッド パネル

スタイルとテンプレート

ココでは、「外観」に関する設定を行う「スタイル」と「テンプレート」について説明する。

スタイル

WPF / Silverlightで外観をカスタマイズする仕組みのこと。

スタイルの基本

「スタイル」は、UI要素の「依存関係プロパティ」の一括管理機能を持ち、これを使用して「外観」に統一性を持たせるための機能である。
「スタイル」は、Style型のオブジェクトであり、各UI要素(FrameworkElement? or FrameworkContentElement?)のStyleプロパティに指定可能である。

  • 外観に関する値を設定する「プロパティ属性構文
    以下は、UI要素の「依存関係プロパティ」に、「外観」に関するFill、Height、Width属性の属性値を設定する「プロパティ属性構文」である。
    • XAML
      <Grid>
        <Ellipse Fill="Blue" Height="80" Width="160"/>
      </Grid>
    • レンダリング結果
      外観に関する値を設定する「プロパティ属性構文」
  • プロパティ属性構文のスタイル化
    • 上記を、「プロパティ要素構文」を使用してStyle型のオブジェクトを生成し、Styleプロパティにこれを指定する例を以下に示す。
      • TargetType?属性により、「スタイル」は指定の型(ここではEllipse要素)に適用される。
      • このTargetType?属性による型指定が無いと、対象のプロパティの有・無などが判別できないため、型指定は必須となっている。
  • XAML
    <Grid>
      <Ellipse>
        <Ellipse.Style>
          <Style TargetType="Ellipse">
            <Setter Property="Fill" Value="Blue"/>
            <Setter Property="Height" Value="80"/>
            <Setter Property="Width" Value="160"/>
          </Style>
        </Ellipse.Style>
      </Ellipse>
    </Grid>
  • レンダリング結果
    プロパティ属性構文のスタイル化
  • スタイルをリソースとして定義
    • なお、上記の「スタイル」化は、通常のプロパティ設定を冗長に記述しているに過ぎず、何の意味もなさない。
    • このため、通常、「スタイル」を「リソース」として定義し、各UI要素のStyleプロパティにはStaticResource?参照を使用して設定する。
  • 以下は、x:Keyを明示した例である。
    • XAML
        <StackPanel.Resources>
          <Style x:Key="EllipseStyle" TargetType="Ellipse">
            <Setter Property="Fill" Value="Blue"/>
            <Setter Property="Height" Value="80"/>
            <Setter Property="Width" Value="160"/>
          </Style>
        </StackPanel.Resources>
        <Ellipse Style="{StaticResource EllipseStyle}"/>
        <Ellipse Style="{StaticResource EllipseStyle}"/>
      </StackPanel>
    • レンダリング結果
      スタイルをリソースとして定義
  • ベースクラスの型にスタイルを適用
    また、派生元(ここではShape型)が同じであれば、異なる要素でも同じ「スタイル」を適用できる。
    • XAML
      <StackPanel>
        <StackPanel.Resources>
          <Style x:Key="ShapeStyle" TargetType="Shape">
            <Setter Property="Fill" Value="Blue"/>
            <Setter Property="Height" Value="80"/>
            <Setter Property="Width" Value="160"/>
          </Style>
        </StackPanel.Resources>
        <Ellipse Style="{StaticResource ShapeStyle}"/>
        <Rectangle Style="{StaticResource ShapeStyle}"/>
      </StackPanel>
    • レンダリング結果
      ベースクラスの型にスタイルを適用
  • スタイルを全体に適用する例
    x:Keyを設定しなければ、TargetType?属性により、「スタイル」は指定の型(ここではEllipse型)、全体に適用される
    (ただし、派生元が同じだが、型の異なる全ての要素に「スタイル」を適用することはできない)。
    • XAML
      <StackPanel>
        <StackPanel.Resources>
          <Style TargetType="Ellipse">
            <Setter Property="Fill" Value="Blue"/>
            <Setter Property="Height" Value="80"/>
            <Setter Property="Width" Value="160"/>
          </Style>
        </StackPanel.Resources>
        <Ellipse/>
        <Ellipse/>
      </StackPanel>
    • レンダリング結果
      スタイルを全体に適用する例

スタイルの継承

  • TargetType? プロパティが同じ2つのStyleクラスを定義し、
    BasedOn?プロパティに継承元となるStyleクラスを設定することで、

    「スタイル」の継承が可能である。

(Silverlight 2のStyleクラスにはBasedOn?プロパティが存在しなかったが、Silverlight 3で追加されている)

  • スタイルの継承例
    • XAML
      <StackPanel>
        <StackPanel.Resources>
          <Style x:Key="EllipseBaseStyle" TargetType="Ellipse">
            <Setter Property="Fill" Value="Blue"/>
            <Setter Property="Height" Value="80"/>
            <Setter Property="Width" Value="160"/>
          </Style>
          <Style x:Key="EllipseInheritedStyle" TargetType="Ellipse"
            BasedOn="{StaticResource EllipseBaseStyle}">
            <Setter Property="Stroke" Value="Yellow"/>
            <Setter Property="StrokeThickness" Value="10"/>
          </Style>
        </StackPanel.Resources>
        <Ellipse Style="{StaticResource EllipseBaseStyle}"/>
        <Ellipse Style="{StaticResource EllipseInheritedStyle}"/>
       </StackPanel>
    • レンダリング結果
      スタイルの継承例

外部ディクショナリ ファイル化

  • 「ディクショナリ ファイル」を使用して、CSSファイルのように、
    「スタイル」を外部ディクショナリ ファイルに定義することも可能である。
  • 以下は、「ディクショナリ ファイル」を使用する例である。
    • XAML
      <Window.Resources>
        <ResourceDictionary Source="Dictionary1.xaml"/>
      </Window.Resources>
      <StackPanel>
        <StackPanel.Resources>
          <Style x:Key="EllipseInheritedStyle" TargetType="Ellipse"
            BasedOn="{StaticResource EllipseBaseStyle}">
            <Setter Property="Stroke" Value="Yellow"/>
            <Setter Property="StrokeThickness" Value="10"/>
          </Style>
        </StackPanel.Resources>
        <Ellipse Style="{StaticResource EllipseBaseStyle}"/>
        <Ellipse Style="{StaticResource EllipseInheritedStyle}"/>
      </StackPanel>
    • XAML ( 外部ディクショナリ ファイル : Dictionary1.xaml
      <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <Style x:Key="EllipseBaseStyle" TargetType="Ellipse">
          <Setter Property="Fill" Value="Blue"/>
          <Setter Property="Height" Value="80"/>
          <Setter Property="Width" Value="160"/>
        </Style>
      </ResourceDictionary>

実行時スタイル

の方法を、「スタイル」でも応用可能である。

  • 上記の例のXAMLを以下のように書き換え、
    コード ビハインドからResourceDictionary?を切り換え
    「スタイル」(スキン)を実行時に、動的に切り換えることができる。
    • XAML
      <StackPanel>
        <Ellipse Style="{DynamicResource MyEllipseStyle}"/>
        <Button Content="青" Height="23" Name="button1" Width="75" Click="button1_Click" />
        <Button Content="赤" Height="23" Name="button2" Width="75" Click="button2_Click" />
      </StackPanel>
    • XAML ( 外部ディクショナリ ファイル :
      • Dictionary1.xaml
        <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
          <Style x:Key="MyEllipseStyle" TargetType="Ellipse">
            <Setter Property="Fill" Value="Blue"/>
            <Setter Property="Height" Value="80"/>
            <Setter Property="Width" Value="160"/>
          </Style>
        </ResourceDictionary>
      • Dictionary2.xaml
        <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
            <Style x:Key="MyEllipseStyle" TargetType="Ellipse">
                <Setter Property="Fill" Value="Red"/>
                <Setter Property="Height" Value="160"/>
                <Setter Property="Width" Value="80"/>
            </Style>
        </ResourceDictionary>
    • コード・ビハインド
      public partial class Window1 : Window {
        public Window1() {
          InitializeComponent();
          this.LoadResourceDictionary("Dictionary1.xaml");
        }
      
        private void button1_Click(object sender, RoutedEventArgs e) {
          this.LoadResourceDictionary("Dictionary1.xaml");
        }
      
        private void button2_Click(object sender, RoutedEventArgs e) {
          this.LoadResourceDictionary("Dictionary2.xaml");
        }
      
        private void LoadResourceDictionary(string name) {
          ResourceDictionary dictionary =
            (ResourceDictionary)Application.LoadComponent(new Uri(name, UriKind.Relative));
          if (dictionary != null) Application.Current.Resources = dictionary;
        }
      }
    • レンダリング結果
      実行時スタイル

テンプレート

WPF / Silverlightで外観をカスタマイズする仕組みのこと。

テンプレートの基本

このため、WPFのUIコントロールはコントロールの「外観」を自由に変更でき、柔軟性が非常に高くなっている。

#コントロールのクラス型コンテンツを設定するプロパティ説明
1ContentControl?クラスContentプロパティContentControl?.Contentプロパティにコントロールの外観を設定できる。
2ItemsControl?クラスItemsプロパティItemsControl?.Itemsプロパティにコントロールの外観を設定できる。
  • また、「テンプレート」により、
    • コントロールの「外観」を宣言型マークアップでカスタマイズ可能になる。
    • また、「イベント ハンドラ」や「イベント トリガ」なども、この「テンプレート」により定義可能で、
      「テンプレート」を「スタイル」化、「リソース」化することで、任意の型のコントロールに、これらの「テンプレート」の定義の適用を強制できる。
  • 「テンプレート」は、コントロールの用途毎に、複数のものが用意されている。
    • テンプレートを設定するのプロパティと、テンプレートの型
      #テンプレートを設定するプロパティテンプレートのクラス型説明
      1Control.TemplateControlTemplateControlコントロールの全体の外観をカスタマイズする「テンプレート」を定義する際に使用する。
      各種プロパティを表示する場合、各種プロパティを表示するための要素をControlTemplate?クラスに含める。
      1-1ContentControl?の(Control. )TemplateプロパティContentプロパティを表示する場合、ContentControl?.Contentプロパティを表示するContentPresenter?クラスをControlTemplate?クラスに含める。
      1-2ItemsControl?の(Control. )TemplateプロパティItemsプロパティを表示する場合、ItemsControl?.Itemsプロパティを表示するItemsPresenter?クラスをControlTemplate?クラスに含める。
      2XXXX.XXXXTemplateDataTemplateContentControl?ItemsControl?コントロールのデータの外観をカスタマイズする「テンプレート」を定義する際に使用する。
      各種データを表示する場合、各種データを表示するための要素をDataTemplate?クラスに含める。
      2-1ContentControl.ContentTemplateContentControl?.ContentTemplate?プロパティには、各種データを表示するための要素を含めたDataTemplate?クラスを設定する。
      2-2ItemsControl.ItemTemplateItemsControl?.ItemTemplate?プロパティには、各種データを表示するための要素を含めたDataTemplate?クラスを設定する。
      3その他種々のコントロールの個別領域の外観をカスタマイズする「テンプレート」を設定する際に使用する。
      3-1ItemsControl.ItemsPanelItemsPanelTemplateItemsプロパティに設定したコレクションの並びをカスタマイズできる。
      3-2HeaderedContentControl.HeaderTemplateDataTemplateヘッダ・データの表示をカスタマイズできる。
      3-3GridViewColumn.CellTemplateセル・データの表示をカスタマイズできる。
  • ただし、「テンプレート」で作成できる「外観」は、静的なものに限られる。

ControlTemplate?DataTemplate?

  • ControlTemplate?
    • Control自身の見た目を決定するもの。
    • 多くの場合TemplateBinding?を使い、テンプレート親とのコントロールとしてのデータとのバインドを作成する。
  • DataTemplate?
    • Controlに割り当てられたデータの見た目を定義するもの。
    • 多くの場合Bindingを使い、割り当てられたデータとのバインドを作成する。
  • ControlTemplate?DataTemplate?の使い分け
  • その1
    • ControlTemplate?
      カスタマイズしたいものがコントロール自身である場合にを利用する。
    • DataTemplate?
      カスタマイズしたいものが割り当てられるデータである場合にを利用する。
  • その2
    状況によっては、ControlTemplate?DataTemplate?
    どちらを使っても可能なこともあるので、再利用性で考える。
    • ControlTemplate?
      そのカスタマイズした結果できるであろうコントロールに、別のデータを載せることがある場合。
    • DataTemplate?
      そのカスタマイズした結果できるであろうコントロールに、別のデータを載せることがない場合。
  • その3
    • ControlTemplate?
      ContentControl?をカスタマイズする場合。
    • DataTemplate?
      ItemsControl?をカスタマイズする場合。

「テンプレート」値を反映させる方法

また、親コントロールに適用した、「テンプレート」の値を
Contentプロパティ(またはItemsプロパティ)を含む
プロパティ値に反映させるにめには、下記の3通りの方法を選択的に使用できる。

  • 「プレゼンター」を利用する方法
  • プレゼンター : ScrollContentPresenter?, CellValuePresenter?、etc.

    参考 : 【WPF】Presenter(?)クラスを収集と考察。 | 創造的プログラミングと粘土細工
    http://pro.art55.jp/?eid=931821

  • 同様に、RelativeSource の「マークアップ拡張」も使用できる。
    <Binding RelativeSource="{RelativeSource TemplatedParent}" .../>
    • なお、「データ バインディング」で使用する場合は、次のようになる。
      <object property="{Binding RelativeSource={RelativeSource TemplatedParent} ...}" .../>
    • RelativeSource?の例
      • XAML
        <StackPanel>
        
          <TextBlock Text="{Binding RelativeSource
            ={RelativeSource Self}, Path=FontFamily}" />・・・(1)
        
          <Border Background="Black" Height="5"/>
        
          <TextBlock Text="{Binding RelativeSource
            ={RelativeSource AncestorType={x:Type StackPanel}},Path=Orientation}" />・・・(2)
        
          <Border Background="Black" Height="5"/>
        
          <Button Content="Hello world">
            <Button.Template>
              <ControlTemplate TargetType="{x:Type Button}">
                <ContentPresenter Content="{Binding RelativeSource
                  ={RelativeSource TemplatedParent}, Path=Content}" />・・・(3)
              </ControlTemplate>
            </Button.Template>
          </Button>
        
        </StackPanel>
      • レンダリング結果
        RelativeSource
  • 説明
    • (1) では、TextBlock?要素自身のFontFamily?プロパティを取得して、自身のTextプロパティに設定している。
    • (2) では、親要素(AncestorType? 属性に指定されているStackPanel?型)を検索し、Orientationプロパティを取得して、自身のTextプロパティに設定している。
    • (3) では、TemplatedParent? 属性により、テンプレートを適用しているControlTemplate?(ここでは、ControlTemplate?であるButton要素)の
      Contentプロパティを取得し、ContentPresenter?クラスのContentプロパティに設定している。

参考

テンプレートの例

ContentControl?のテンプレート(ControlTemplate?)の例

  • XAML
    • 以下は、ControlTemplate?ContentPresenter?を使用したContentControl?(Button)コントロールの例である。
      <Grid>
        <Button Margin="5" Width="100" Height="100" Content="ボタン">
          <Button.Template>
            <ControlTemplate TargetType="Button">
              <Grid>
                <Rectangle Fill="Blue"/>
                <Ellipse Fill="Red"/>
                <ContentPresenter
                  HorizontalAlignment="Center"
                  VerticalAlignment="Center"/>
              </Grid>
            </ControlTemplate>
          </Button.Template>
        </Button>
      </Grid>
    • 上記のContentPresenter?を、「マークアップ拡張」のTemplateBinding?に書き換えた例
      <Grid>
        <Button Margin="5" Width="100" Height="100" Content="ボタン">
          <Button.Template>
            <ControlTemplate TargetType="Button">
              <Grid>
                <Rectangle Fill="Blue"/>
                <Ellipse Fill="Red"/>
                <TextBlock
                  Text="{TemplateBinding Content}"
                  HorizontalAlignment="Center" VerticalAlignment="Center"/>
              </Grid>
            </ControlTemplate>
          </Button.Template>
        </Button>
      </Grid>
    • 上記のTemplateBinding?を、「マークアップ拡張」のRelativeSource?に書き換えた例。
      なお、RelativeSource?の場合、TargetType?属性を消しても上手く動作する現象を確認できた。
      <Grid>
        <Button Margin="5" Width="100" Height="100" Content="ボタン">
          <Button.Template>
            <ControlTemplate>
              <Grid>
                <Rectangle Fill="Blue"/>
                <Ellipse Fill="Red"/>
                <TextBlock
                  Text="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}"
                  HorizontalAlignment="Center" VerticalAlignment="Center"/>
              </Grid>
            </ControlTemplate>
          </Button.Template>
        </Button>
      </Grid>
  • レンダリング結果
    ContentControlのControlTemplate
  • スタイル化
    • 「テンプレート」は、以下のように「スタイル」に組み込むことも可能である。
      • XAML
        <Window.Resources>
          <Style x:Key="buttonTemplate" TargetType="Button">
            <Setter Property="Template">
              <Setter.Value>
                <ControlTemplate TargetType="Button">
                  <Grid>
                    <Rectangle Fill="Blue"/>
                    <Ellipse Fill="Red"/>
                    <ContentPresenter
                      HorizontalAlignment="Center"
                      VerticalAlignment="Center"/>
                  </Grid>
                </ControlTemplate>
              </Setter.Value>
            </Setter>
          </Style>
        </Window.Resources>
        <StackPanel Orientation="Vertical">
          <Button Margin="5" Width="100" Height="100">ボタン1</Button>
          <Button Margin="5" Width="100" Height="100">ボタン2</Button>
          <Button Margin="5" Width="100" Height="100"
            Style="{StaticResource buttonTemplate}">ボタン3</Button>
        </StackPanel>
      • レンダリング結果
        ControlTemplateのスタイル化 (1)
  • なお、x:Keyを設定しなければ、TargetType?属性により、
    「スタイル」は指定の型(ここではButtonコントロール)、全体に適用される。
    • XAML
      <Window.Resources>
        <Style TargetType="Button">
          <Setter Property="Template">
            <Setter.Value>
              <ControlTemplate TargetType="Button">
                <Grid>
                  <Rectangle Fill="Blue"/>
                  <Ellipse Fill="Red"/>
                  <ContentPresenter
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"/>
                </Grid>
              </ControlTemplate>
            </Setter.Value>
          </Setter>
        </Style>
      </Window.Resources>
      <StackPanel Orientation="Vertical">
        <Button Margin="5" Width="100" Height="100">ボタン1</Button>
        <Button Margin="5" Width="100" Height="100">ボタン2</Button>
        <Button Margin="5" Width="100" Height="100">ボタン3</Button>
      </StackPanel>
    • レンダリング結果
      ControlTemplateのスタイル化 (2)
  • リソース化
    • 「テンプレート」と「スタイル」を別々の「リソース」として定義し、
      StaticResource?参照の「マークアップ拡張」により
      「テンプレート」を「スタイル」に組み込むことも可能である。
      • XAML
        <Window.Resources>
          <ControlTemplate x:Key="buttonTemplate" TargetType="Button">
            <Grid>
              <Rectangle Fill="Blue"/>
              <Ellipse Fill="Red"/>
              <ContentPresenter
                HorizontalAlignment="Center"
                VerticalAlignment="Center"/>
            </Grid>
          </ControlTemplate>
          <Style TargetType="Button">
            <Setter Property="Template" Value="{StaticResource buttonTemplate}"/>
          </Style>
        </Window.Resources>
        <StackPanel Orientation="Vertical">
          <Button Margin="5" Width="100" Height="100">ボタン1</Button>
          <Button Margin="5" Width="100" Height="100">ボタン2</Button>
          <Button Margin="5" Width="100" Height="100">ボタン3</Button>
        </StackPanel>
    • 以下、同様に、「ディクショナリ ファイル」を使用して、CSSファイルのように、
      「テンプレート」を外部ファイルに定義することも可能である。
      • XAML
        <Window.Resources>
          <ResourceDictionary Source="TemplateStyleDictionary.xaml"/>
        </Window.Resources>
        <StackPanel Orientation="Vertical">
          <Button Margin="5" Width="100" Height="100">ボタン1</Button>
          <Button Margin="5" Width="100" Height="100">ボタン2</Button>
          <Button Margin="5" Width="100" Height="100">ボタン3</Button>
        </StackPanel>
      • XAML ( 外部ディクショナリ ファイル : TemplateStyleDictionary?.xaml
        <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
          <ControlTemplate x:Key="buttonTemplate" TargetType="Button">
            <Grid>
              <Rectangle Fill="Blue"/>
              <Ellipse Fill="Red"/>
              <ContentPresenter
                HorizontalAlignment="Center"
                VerticalAlignment="Center"/>
            </Grid>
          </ControlTemplate>
          <Style TargetType="Button">
            <Setter Property="Template" Value="{StaticResource buttonTemplate}"/>
          </Style>
        </ResourceDictionary>
    • レンダリング結果
      ControlTemplateのスタイル化 (2)

ContentControl?のテンプレート(DataTemplate?)の例

  • 以下は、ControlTemplate?ContentPresenter?DataTemplate?を使用したContentControl?(Button)コントロールの例である。
  • XAML
    <Grid>
      <Button Content="Click Here"
        Margin="5" Width="100" Height="100">
        <Button.Template>
          <ControlTemplate TargetType="Button">
            <Grid>
              <Rectangle Fill="Blue"/>
              <Ellipse Fill="Red"/>
              <ContentPresenter
                HorizontalAlignment="Center"
                VerticalAlignment="Center"/>
            </Grid>
          </ControlTemplate>
        </Button.Template>
        <Button.ContentTemplate>
          <DataTemplate>
            <Grid>
              <TextBlock Text="{Binding}"/>
            </Grid>
          </DataTemplate>
        </Button.ContentTemplate>
      </Button>
    </Grid>
  • レンダリング結果
    ContentControlのDataTemplate
  • 上記の通り、DataTemplate?内でContentプロパティのデータを使用する場合は、"{Binding}"と記述するだけで良い。
  • DataTemplate?のポイント
    • 通常、ContentControl?内にはContentControl?.Contentプロパティを表示するだけの場合が多いので、
      ControlTemplate?だけで事足りることが多く、DataTemplate?の利用機会は少ない。
  • なお、ControlTemplate? + ContentPresenter?DataTemplate?は、
    必ずしも併用しなくても良い(DataTemplate?のみの利用も可能である)。
  • DataTemplate?内でContentプロパティ以外のデータを使用する場合は、例えば、
    <TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=xxxx}"/>

などの記述方法がある。

ItemsControl?のテンプレート(DataTemplate?)の例

  • 以下は、ItemsControl?.ItemTemplate?プロパティを使用したListBox?コントロールの例である。
  • 基本的に、
    ItemsControl?コントロールに「データ バインディング」を行った際の「外観」のカスタマイズには、
    ItemsControl?.ItemTemplate?プロパティへ、DataTemplate?を指定することで行う。
  • コード
  • XAML
    <Grid>
      <StackPanel x:Name="stackPanel1">
        <ListBox x:Name="ListBox1" ItemsSource="{Binding}">
          <ListBox.ItemTemplate>
            <DataTemplate>
              <Border BorderBrush ="Black" BorderThickness ="1" Margin="5">
                <StackPanel Orientation="Horizontal" Height="120" Width="250">
                  <Image Height="100" Source="{Binding Path=[Image]}" />
                  <TextBlock Text="{Binding Path=[Text]}"  VerticalAlignment="Center" />
                </StackPanel>
              </Border>
            </DataTemplate>
          </ListBox.ItemTemplate>
        </ListBox>
      </StackPanel>
    </Grid>
  • コード ビハインド
    public Window1() {
      InitializeComponent();
    
      List<Dictionary<string, string>> lst
        = new List<Dictionary<string, string>>();
    
      Dictionary<string, string> dic = null;
    
      dic = new Dictionary<string, string>();
      dic["Image"] = @".\Blue hills.jpg";
      dic["Text"] = "Blue hills";
      lst.Add(dic);
    
      dic = new Dictionary<string, string>();
      dic["Image"] = @".\Sunset.jpg";
      dic["Text"] = "Sunset";
      lst.Add(dic);
    
      dic = new Dictionary<string, string>();
      dic["Image"] = @".\Water lilies.jpg";
      dic["Text"] = "Water lilies";
      lst.Add(dic);
    
      dic = new Dictionary<string, string>();
      dic["Image"] = @".\Winter.jpg";
      dic["Text"] = "Winter";
      lst.Add(dic);
    
      this.stackPanel1.DataContext = lst;
    }
  • 補足
    補足となるが、選択したオブジェクトは、以下のコードで取得できる。
    Dictionary<string, string> dic = (Dictionary<string, string>)this.ListBox1.SelectedValue;
  • なお、「バインディング ソース」にDataTable?を使用する場合は、次のように記述できる。
  • DataTable?DataColumn?ColumnName?プロパティをPath属性に指定する際は、
    「通常のプロパティの場合の記述方法」・「インデクサの場合の記述方法」の両方の指定が可能である。
  • また、ここでは、「テンプレート」を「リソース」として定義し、StaticResource?参照により「スタイル」に指定している。
  • コード
  • XAML
    <Window.Resources>
      <DataTemplate x:Key="listBoxStyle">
        <Border BorderBrush ="Black" BorderThickness ="1" Margin="5">
          <StackPanel Orientation="Horizontal" Height="120" Width="250">
            <Image Height="100" Source="{Binding Path=Image}" /> ・・・ 通常のプロパティの場合の記述方法
            <TextBlock Text="{Binding Path=[Text]}"  VerticalAlignment="Center" /> ・・・ インデクサの場合の記述方法
          </StackPanel>
        </Border>
      </DataTemplate>
    </Window.Resources>
    <Grid>
      <StackPanel x:Name="stackPanel1">
        <ListBox x:Name="ListBox1"
          ItemsSource="{Binding}" ItemTemplate="{StaticResource listBoxStyle}"/>
      </StackPanel>
    </Grid>
  • コード ビハインド
    public Window1() {
      InitializeComponent();
    
      DataTable dt = new DataTable();
    
      dt.Columns.Add(new DataColumn("Image"));
      dt.Columns.Add(new DataColumn("Text"));
    
      DataRow dr = null;
    
      dr = dt.NewRow();
      dr["Image"] = @".\Blue hills.jpg";
      dr["Text"] = "Blue hills";
      dt.Rows.Add(dr);
    
     ・・・
    
      dr = dt.NewRow();
      dr["Image"] = @".\Winter.jpg";
      dr["Text"] = "Winter";
      dt.Rows.Add(dr);
    
      this.stackPanel1.DataContext = dt;
    }
  • 補足
    補足となるが、選択したオブジェクトは、以下のコードで取得できる。
    DataRow dr = (DataRow)this.ListBox1.SelectedValue;
  • レンダリング結果
    ItemsControlのDataTemplate

ItemsControl?DisplayMemberPath?, SelectedValuePath?)の例

  • ここまで、ItemsControl?.ItemTemplate?プロパティへ、DataTemplate?を指定する方法について述べたが、
    代表的なItemsControl?コントロールであるListBox?ComboBox?(Selector から派生するクラス)は、
    DisplayMemberPath?SelectedValuePath?などの属性を持っている。
  • このため、単純な表示であればItemsControl?.ItemTemplate?プロパティへ、DataTemplate?を指定しなくても、
    「データ バインディング」したオブジェクトから「表示項目」と「データ項目」を分離できる。
  • 以下、その例を示す。
    • XAML
      <Grid>
        <StackPanel x:Name="stackPanel1">
          <ListBox x:Name="ListBox1" ItemsSource="{Binding}"
            SelectedValuePath="Value" DisplayMemberPath="Display" />
          <Button Click="Button_Click">選択値</Button>
        </StackPanel>
      </Grid>
    • コード ビハインド
      public Window1() {
        InitializeComponent();
      
        DataTable dt = new DataTable();
        dt.Columns.Add(new DataColumn("Value"));
        dt.Columns.Add(new DataColumn("Display"));
      
        DataRow dr = null;
      
        dr = dt.NewRow();
        dr["Value"] = "0001";
        dr["Display"] = "0001の表示";
        dt.Rows.Add(dr);
      
       ・・・
      
        dr = dt.NewRow();
        dr["Value"] = "0004";
        dr["Display"] = "0004の表示";
        dt.Rows.Add(dr);
      
        this.stackPanel1.DataContext = dt;
      }
    • レンダリング結果
      SelectedValuePath,DisplayMemberPath属性の利用 (1)
  • DisplayMemberPath?属性に指定されたデータが表示項目となり、
    SelectedValuePath?属性に指定されたデータがSelectedValue?プロパティで取得される、選択時のデータ項目となる。
    以下は、選択されたデータを取得するコードの例である。
  • コード ビハインド
    private void Button_Click(object sender, RoutedEventArgs e) {
      MessageBox.Show(this.ListBox1.SelectedValue.ToString());
    }
  • 選択されたデータ
    SelectedValuePath,DisplayMemberPath属性の利用 (2)

その他のItemsControl?のテンプレートの例

  • ControlTemplate?を使用したItemsControl?
    • 以下は、ControlTemplate?を使用したItemsControl?ListBox?)コントロールの例である。
    • 通常、ItemsControl?ではDataTemplate?だけで事足りることが多いので、ControlTemplate?の利用機会は少ないが、
      ここではControlTemplate?内でItemsPresenter?を使用してItemsプロパティのデータの中央揃えに利用している。
    • XAML
      <Grid>
        <ListBox>
          <ListBox.Template>
            <ControlTemplate>
              <ItemsPresenter Margin="5" HorizontalAlignment="Center"/>
            </ControlTemplate>
          </ListBox.Template>
          <ListBoxItem>1</ListBoxItem>
          <ListBoxItem>2</ListBoxItem>
          <ListBoxItem>3</ListBoxItem>
        </ListBox>
      </Grid>
    • レンダリング結果
      ItemsControlとControlTemplate
  • ItemsPanelTemplate?を使用したItemsControl?
    • Itemsプロパティのデータの並び(レイアウト)は、
      ItemsControl?.ItemsPanel? プロパティにItemsPanelTemplate? を設定することで変更できる。
    • ここではStackPanel?を使用し、各アイテムの並びを横並びにしている。
    • XAML
      <Grid>
        <ListBox>
          <ListBox.Template>
            <ControlTemplate TargetType="ListBox">
              <ItemsPresenter Margin="5" HorizontalAlignment="Center"/>
            </ControlTemplate>
          </ListBox.Template>
          <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
              <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
          </ListBox.ItemsPanel>
          <ListBoxItem>1</ListBoxItem>
          <ListBoxItem>2</ListBoxItem>
          <ListBoxItem>3</ListBoxItem>
        </ListBox>
      </Grid>
    • レンダリング結果
      ItemsControlとItemsPanelTemplate
  • ヘッダの追加(ControlTemplate?
    • ItemsControl?では、ControlTemplate?は(繰り返し項目以外の)ヘッダなどの「外観」を作成するなどの用途で使用することが可能である。
    • 以下、ヘッダをControlTemplate?で実装し、データをDataTemplate?で実装した例である。
    • コード
      • XAML
        <Grid>
          <ListBox Name="listBox1" ItemsSource="{Binding}">
            <ListBox.Template>
              <ControlTemplate>
                <StackPanel>
                  <StackPanel  Orientation="Horizontal" Background="Gray">
                    <TextBlock Width="100" Text="ヘッダ1"/>
                    <TextBlock Width="100" Text="ヘッダ2"/>
                  </StackPanel>
                  <ItemsPresenter/>	・・・ItemsPresenterを忘れないよう設定する。
                </StackPanel>
              </ControlTemplate>
            </ListBox.Template>
            <ListBox.ItemTemplate>
              <DataTemplate>
                <StackPanel  Orientation="Horizontal">
                  <TextBlock Width="100" Text="{Binding Path=[data1]}"/>
                  <TextBlock Width="100" Text="{Binding Path=[data2]}"/>
                </StackPanel>
              </DataTemplate>
            </ListBox.ItemTemplate>
          </ListBox>
        </Grid>
      • コード ビハインド
        public Window1() {
          InitializeComponent();
        
          List<Dictionary<string, string>> lst = new List<Dictionary<string, string>>();
          Dictionary<string, string> dic = null;
        
          dic = new Dictionary<string, string>();
          dic["data1"] = "データ11";
          dic["data2"] = "データ11";
          lst.Add(dic);
        
          dic = new Dictionary<string, string>();
          dic["data1"] = "データ21";
          dic["data2"] = "データ22";
          lst.Add(dic);
        
          this.listBox1.DataContext = lst; 
        }
    • レンダリング結果
      ControlTemplateによるヘッダの追加
  • グリッド系のコントロールには、ヘッダ専用の「テンプレート」が準備されているものもある。
  • 各種テンプレートの組み合わせ
    以下は、DataTemplate?ControlTemplate?ItemsPanelTemplate?を使用し、
    ItemsControlのテンプレート(DataTemplate)の例を書き直した
    ItemsControl?ListBox?)コントロールの例である(中央揃え+横並び)。
    • コード
      • XAML
        <Grid>
          <StackPanel x:Name="stackPanel1">
            <Button x:Name="button1" Click="button1_Click">選択</Button>
            <ListBox x:Name="ListBox1" ItemsSource="{Binding}">
        
              <ListBox.Template>
                <ControlTemplate>
                  <ItemsPresenter Margin="5" HorizontalAlignment="Center"/>
                </ControlTemplate>
              </ListBox.Template>
        
              <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                  <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
              </ListBox.ItemsPanel>
        
              <ListBox.ItemTemplate>
                <DataTemplate>
                  <Border BorderBrush ="Black" BorderThickness ="1" Margin="5">
                    <StackPanel Orientation="Horizontal" Height="120" Width="250">
                      <Image Height="100" Source="{Binding Path=[Image]}" />
                      <TextBlock Text="{Binding Path=[Text]}"  VerticalAlignment="Center" />
                    </StackPanel>
                  </Border>
                </DataTemplate>
              </ListBox.ItemTemplate>
        
            </ListBox>
          </StackPanel>
        </Grid>
      • コード ビハインド
        public Window1() {
          InitializeComponent();
        
          DataTable dt = new DataTable();
        
          dt.Columns.Add(new DataColumn("Image"));
          dt.Columns.Add(new DataColumn("Text"));
        
          DataRow dr = null;
        
          dr = dt.NewRow();
          dr["Image"] = @".\Blue hills.jpg";
          dr["Text"] = "Blue hills";
          dt.Rows.Add(dr);
        
         ・・・
        
          dr = dt.NewRow();
          dr["Image"] = @".\Winter.jpg";
          dr["Text"] = "Winter";
          dt.Rows.Add(dr);
        
          this.stackPanel1.DataContext = dt;
        }
        
        private void Button_Click(object sender, RoutedEventArgs e) {
          MessageBox.Show(this.ListBox1.SelectedValue.ToString());
        }
    • レンダリング結果
      ItemsControlの各種テンプレートの組み合わせ利用

グリッド系コントロールの例

  • ヘッダ・セルのカスタマイズ
    以下、ListView?コントロールのヘッダとセルのカスタマイズの例を示す。
    • コード
      • XAML
        <Window.Resources>
          <Style x:Key="listViewItemStyle" TargetType="ListViewItem" >
            <Setter Property="BorderBrush" Value="Gray"/>
            <Setter Property="BorderThickness" Value="2"/>
          </Style>
          <DataTemplate x:Key="listViewHeaderTemplate">
            <TextBlock FontSize="16" Foreground="Navy" Text="{Binding}"/>
          </DataTemplate>
          <DataTemplate x:Key="listViewCellStyle">
            <Border BorderBrush ="Black" BorderThickness ="5" Margin="5">
              <Image Height="100" Source="{Binding Path=Image}" />
            </Border>
          </DataTemplate>
        </Window.Resources>
        <StackPanel x:Name="stackPanel1">
          <ListView x:Name="listView1" ItemsSource="{Binding}"
            ItemContainerStyle="{StaticResource listViewItemStyle}">
            <ListView.View>
              <GridView ColumnHeaderTemplate="{StaticResource listViewHeaderTemplate}">
                <GridViewColumn Header="名称" 
                  DisplayMemberBinding="{Binding Path=Text}" />
                <GridViewColumn Header="画像"
                  CellTemplate="{StaticResource listViewCellStyle}"/>
              </GridView>
            </ListView.View>
          </ListView>
        </StackPanel>
      • コード ビハインド
        public Window1() {
          InitializeComponent();
        
          DataTable dt = new DataTable();
        
          dt.Columns.Add(new DataColumn("Image"));
          dt.Columns.Add(new DataColumn("Text"));
        
          DataRow dr = null;
        
          dr = dt.NewRow();
          dr["Image"] = @".\Blue hills.jpg";
          dr["Text"] = "Blue hills";
          dt.Rows.Add(dr);
        
         ・・・
        
          this.stackPanel1.DataContext = dt;
        }
    • レンダリング結果
      ヘッダの追加(グリッド系コントロール)
  • グリッド系のコントロール(ListView?GridView?)は、以下のカスタマイズが可能である。
    • GridViewColumn? クラスを使用した列の定義、
    • GridViewColumn?.CellTemplate? プロパティへの列「テンプレート」設定
    • GridView?.ColumnHeaderTemplate? プロパティへのヘッダ「テンプレート」設定
  • セルにComboBox?を入れる
    • ListView?コントロールのセルにComboBox?を入れることも可能である。
      この場合、列「テンプレート」を定義して、GridViewColumn?.CellTemplate?プロパティへ設定すれば良い。
    • この際、「データ バインディング」を使用してComboBox?を初期化する場合は、
      StaticResource?を使用した「データ バインディング」にて行う必要がある。
      このため、ComboBox?ボックス毎に「リソース」に定義するクラス型を作成する必要がある。
    • この処理を実装する際のポイントは、以下のとおりである。
      • ComboBox?ボックス毎に「リソース」に定義するクラス型を作成する必要があるので、
        基本クラスなどを用いて、このクラス型の定義を簡単に行えるようにする。
      • 上記で作成したComboBox?ボックス毎に「リソース」に定義するクラス型にデータをロードする処理は、
        各クラスのコンストラクタに実装する必要があるが、3層C/S型システムなどでAPサーバからデータ取得を行う場合、
        ラウンドトリップが多くなってしまうことがあるため、集約してデータを取得できるように、処理を工夫する必要がある。
    • サンプル
      https://github.com/OpenTouryoProject/SampleProgram/tree/master/UISubsystem/WPF/Cbx%20in%20DataGrid

トリガ

  • トリガは、
    • Style 、ControlTemplate?DataTemplate? などの、
      UIElementの視覚化を指定するオブジェクトのプロパティとして設定し、
    • 「ある事象」をきっかけにして、そのUIElementの視覚設定を変更する

ものである。

  • トリガには、以下の3種類が存在し、それぞれ、きっかけとなる事象の種類が異なる。
    • プロパティ トリガ
      きっかけ:上記の適用されているUIElementのプロパティの変更
    • データ トリガ
      きっかけ:UIElementにバインドされたデータのプロパティの変更
    • イベント トリガ
      きっかけ:上記の適用されているUIElementでハンドルできる「ルーティング イベント」の検知
  • 以下、それぞれのトリガについて説明する。

プロパティ トリガ

  • プロパティ トリガは、UIElementのプロパティ値に基づく。
  • データトリガには、Triggerと、MultiTrigger?の2つがある。
  • 以下は、プロパティ トリガとStyleを組み合わせて利用した例である。
    • XAML
      <Window.Resources>
        <Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
          <Setter Property="Background" Value="LightYellow" />
          <!--IsPressed = True で、背景色変更-->
          <Style.Triggers>
            <Trigger Property="IsPressed" Value="True">
              <Setter Property="Background" Value="LightBlue" />
            </Trigger>
          </Style.Triggers>
        </Style>
      
        <Style x:Key="ButtonStyle2" TargetType="{x:Type Button}">
          <Setter Property="Background" Value="LightYellow" />
          <!--IsMouseOver = True で、背景色変更-->
          <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
              <Setter Property="Background" Value="LightBlue" />
            </Trigger>
          </Style.Triggers>
        </Style>
      
        <Style x:Key="ButtonStyle3" TargetType="{x:Type Button}">
          <Setter Property="Background" Value="LightYellow" />
          <!--IsMouseOver、IsFocused = True で、背景色変更-->
          <Style.Triggers>
            <MultiTrigger>
              <MultiTrigger.Conditions>
                <Condition Property="IsMouseOver" Value="True"/>
                <Condition Property="IsFocused" Value="True"/>
              </MultiTrigger.Conditions>
              <Setter Property="Background" Value="LightBlue" />
            </MultiTrigger >
          </Style.Triggers>
        </Style>
      </Window.Resources>
      
      <Grid>
        <Grid.RowDefinitions>
                  <RowDefinition />
                  <RowDefinition />
                  <RowDefinition />
        </Grid.RowDefinitions>
        <Button Style="{StaticResource ButtonStyle1}" Grid.Row="0" >Button(IsPressed)</Button>
        <Button Style="{StaticResource ButtonStyle2}" Grid.Row="1" >Button(IsMouseOver)</Button>
        <Button Style="{StaticResource ButtonStyle3}" Grid.Row="2" >Button(IsMouseOver & IsFocused)</Button>
      </Grid>
    • レンダリング結果
      プロパティ トリガ

データ トリガ

  • データ トリガは、UIElementのプロパティ値ではなく、
    バインドされたデータのプロパティ値に基づく。
  • データトリガには、DataTrigger?と、MultiDataTrigger?の2つがある。
  • 以下は、データ トリガとStyleを組み合わせて利用した例である。
    • コード
      • XAML
        <Window.Resources>
        
          <my:Places x:Key="PlacesData"/>
        
          <Style TargetType="ListBoxItem">
            <Style.Triggers>
        
              <!--DataTrigger(Condition×1)-->
              <!-- State = WA の行は文字色 = 赤-->
              <DataTrigger Binding="{Binding Path=State}" Value="WA">
                <Setter Property="Foreground" Value="Red" />
              </DataTrigger>
        
              <!--MultiDataTrigger(Condition×n)-->
              <!-- Name = Portland, State = OR の行は背景色 = 黄-->
              <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                  <Condition Binding="{Binding Path=Name}" Value="Portland" />
                  <Condition Binding="{Binding Path=State}" Value="OR" />
                </MultiDataTrigger.Conditions>
                <Setter Property="Background" Value="Yellow" />
              </MultiDataTrigger>
        
            </Style.Triggers>
          </Style>
        
          <DataTemplate DataType="{x:Type my:Place}">
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
              <TextBlock Width="20"/>
              <TextBlock Width="100" Text="{Binding Path=Name}"/>
              <TextBlock Width="20"/>
              <TextBlock Text="{Binding Path=State}"/>
            </StackPanel>
          </DataTemplate>
        
        </Window.Resources>
        
        <StackPanel>
          <TextBlock Margin="5" HorizontalAlignment="Center">Data Trigger Sample</TextBlock>
          <ListBox HorizontalAlignment="Stretch"
                   ItemsSource="{Binding Source={StaticResource PlacesData}}"/>
        </StackPanel>
      • コード ビハインド
        カスタムクラスであるPlaces(前述のXAMLのWindow.Resourcesに定義されている)は、
        StaticResource?から)直接初期化できるように以下のように定義する。
        /// <summary>Place</summary>
        public class Place {
          /// <summary>名前</summary>
          private string _name;
        
          /// <summary>状態</summary>
          private string _state;
        
          /// <summary>名前</summary>
          public string Name {
            get { return _name; }
            set { _name = value; }
          }
        
          /// <summary>状態</summary>
          public string State {
            get { return _state; }
            set { _state = value; }
          }
        
          /// <summary>コンストラクタ</summary>
          public Place(string name, string state) {
            this._name = name;
            this._state = state;
          }
        }
        
        public class Places : ObservableCollection<Place> {
          /// <summary>コンストラクタで</summary>
          public Places() {
            this.Add(new Place("Bellevue", "WA"));
            this.Add(new Place("Gold Beach", "OR"));
            this.Add(new Place("Kirkland", "WA"));
            this.Add(new Place("Los Angeles", "CA"));
            this.Add(new Place("Portland", "ME"));
            this.Add(new Place("Portland", "OR"));
            this.Add(new Place("Redmond", "WA"));
            this.Add(new Place("San Diego", "CA"));
            this.Add(new Place("San Francisco", "CA"));
            this.Add(new Place("San Jose", "CA"));
            this.Add(new Place("Seattle", "WA"));
          }
        }
    • レンダリング結果
      データ トリガ

イベント トリガ

  • 通常、イベント トリガはアニメーション処理開始の指定のために使用する。
  • これについては、「アニメーション」を参照のこと。
  • なお、イベント トリガはFrameworkElement?にも指定することができる 。

Tags: :.NET開発, :UIサブシステム, :WPF/Silverlight, XAML


添付ファイル: fileRenderingResultOfDataTrigger.png 135件 [詳細] fileRenderingResultOfPropertyTrigger.png 99件 [詳細] fileRenderingResultOfGridControlWithHeader.png 119件 [詳細] fileRenderingResultOfVariousTemplatesOfItemsControl.png 147件 [詳細] fileRenderingResultOfControlTemplateOfItemsControlWithHeader.png 121件 [詳細] fileRenderingResultOfItemsPanelTemplate.png 99件 [詳細] fileRenderingResultOfControlTemplateOfItemsControl.png 112件 [詳細] fileRenderingResultOfListBox2.png 87件 [詳細] fileRenderingResultOfListBox1.png 112件 [詳細] fileRenderingResultOfDataTemplateOfItemsControl.png 132件 [詳細] fileRenderingResultOfDataTemplateOfContentControl.png 97件 [詳細] fileRenderingResultOfStyleTemplate2.png 154件 [詳細] fileRenderingResultOfStyleTemplate1.png 115件 [詳細] fileRenderingResultOfControlTemplate.png 124件 [詳細] fileRenderingResultOfRelativeSource.png 145件 [詳細] fileRenderingResultOfStyleAtRuntime.png 116件 [詳細] fileRenderingResultOfInheritedStyle.png 119件 [詳細] fileContentString.png 106件 [詳細] fileStaticResourceAndDictionary.png 109件 [詳細] fileStaticResource.png 108件 [詳細] fileItemsControl.png 108件 [詳細] fileDynamicResourceAndDictionary.png 128件 [詳細] fileDynamicResource2.png 113件 [詳細] fileDynamicResource1.png 126件 [詳細] fileContentStringAndImage.png 114件 [詳細] fileContentImage.png 117件 [詳細] fileAddDictionary.png 144件 [詳細] fileRenderingResultOfStyle5.png 124件 [詳細] fileRenderingResultOfStyle4.png 111件 [詳細] fileRenderingResultOfStyle3.png 136件 [詳細] fileRenderingResultOfStyle2.png 190件 [詳細] fileRenderingResultOfStyle1.png 99件 [詳細] fileRenderingResultOfGridRatio.png 108件 [詳細] fileRenderingResultOfGrid43.png 122件 [詳細] fileRenderingResultOfUniformGrid43.png 139件 [詳細] fileRenderingResultOfUniformGrid34.png 103件 [詳細] fileVerticalRenderingResultOfWrapPanel.png 133件 [詳細] fileHorizontalRenderingResultOfWrapPanel.png 101件 [詳細] fileHorizontalRenderingResultOfStackPanel.png 106件 [詳細] fileVerticalRenderingResultOfStackPanel.png 95件 [詳細] fileRenderingResultOfDockPanel.png 121件 [詳細] fileRenderingResultOfCanvas.png 98件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-05-28 (日) 18:44:26 (759d)