前回は、添付プロパティについて見ました。
今回は、XAML の属性値に波かっこで書くマークアップ拡張を整理します。
WPF の XAML を読んでいると、次のような記述が頻繁に出てきます。
<TextBox Text="{Binding UserName}" />
<Button Style="{StaticResource PrimaryButtonStyle}" />
この {...} の部分が、マークアップ拡張です。
マークアップ拡張とは
通常の属性値は、文字列として書かれ、必要に応じて型変換されます。
<Button Content="保存"
Width="120"
Background="LightBlue" />
この場合、保存、120、LightBlue は、それぞれプロパティが求める型に変換されます。
一方、マークアップ拡張は、単純な文字列では表せない値を作ったり、別の場所から参照したりするための仕組みです。
<TextBox Text="{Binding UserName}" />
この例では、Text プロパティに固定文字列の UserName を設定しているのではありません。UserName というプロパティにデータバインディングしています。
波かっこが出てきたら、「これは単純な文字列ではなく、特別な方法で値を用意している」と考えます。
基本形
マークアップ拡張の基本形は、次のようになります。
{拡張名 引数}
たとえば、次のように書きます。
{Binding UserName}
Binding が拡張名で、UserName が指定内容です。
複数の指定を書く場合は、カンマで区切ります。
<TextBox Text="{Binding UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
この例では、UserName にバインディングし、さらにバインディングの動作を指定しています。
Binding
WPF で最もよく使うマークアップ拡張のひとつが Binding です。
<TextBlock Text="{Binding DisplayName}" />
これは、TextBlock の Text プロパティを、データコンテキストにある DisplayName プロパティと結び付けます。
たとえば、画面の DataContext に次のようなオブジェクトが設定されているとします。
public class UserViewModel
{
public string DisplayName { get; set; } = "田中 太郎";
}
このとき、XAML の TextBlock には DisplayName の値が表示されます。
<TextBlock Text="{Binding DisplayName}" />
Binding は、画面とデータを結び付けるための重要な仕組みです。WPF の MVVM パターンでも中心的に使われます。
StaticResource
StaticResource は、あらかじめ定義したリソースを参照するために使います。
<Window.Resources>
<SolidColorBrush x:Key="AccentBrush"
Color="SteelBlue" />
</Window.Resources>
定義したリソースは、次のように参照できます。
<Button Content="保存"
Background="{StaticResource AccentBrush}" />
この場合、Background プロパティに、AccentBrush というキーを持つリソースが設定されます。
リソースを使うと、色、余白、スタイル、テンプレートなどを共通化できます。
<Window.Resources>
<Style x:Key="PrimaryButtonStyle"
TargetType="Button">
<Setter Property="Padding"
Value="16,8" />
<Setter Property="Background"
Value="SteelBlue" />
<Setter Property="Foreground"
Value="White" />
</Style>
</Window.Resources>
<Button Content="保存"
Style="{StaticResource PrimaryButtonStyle}" />
StaticResource は、読み込み時にリソースを解決する参照です。通常のスタイルやブラシの参照では、まず StaticResource を使う場面が多いです。
DynamicResource
DynamicResource もリソースを参照するためのマークアップ拡張です。
<Button Background="{DynamicResource AccentBrush}" />
StaticResource と似ていますが、DynamicResource はリソースの変更を後から反映できます。
たとえば、テーマ切り替えなどでリソースの値が実行中に変わる場合、DynamicResource を使うことで変更を画面に反映できます。
基本的な使い分けは次のようになります。
| 拡張 | 使う場面 |
|---|---|
StaticResource | 通常のリソース参照 |
DynamicResource | 実行中にリソース変更を反映したい場合 |
迷った場合は、まず StaticResource を使うと考えてよいでしょう。
x:Static
x:Static は、静的メンバーを参照するために使います。
<TextBlock FontFamily="{x:Static SystemFonts.CaptionFontFamily}" />
この例では、SystemFonts.CaptionFontFamily という静的プロパティを参照しています。
列挙値を明示したい場合にも使えます。
<TextBlock TextAlignment="{x:Static TextAlignment.Center}" />
ただし、列挙値は通常、属性値として直接書けます。
<TextBlock TextAlignment="Center" />
そのため、x:Static は、通常の属性値や型変換では表しにくい静的メンバーを参照したいときに使うもの、と考えるとよいでしょう。
x:Null
x:Null は、null を表すために使います。
<Button ToolTip="{x:Null}"
Content="保存" />
XAML の属性値に何も書かないことと、null を明示的に設定することは同じとは限りません。
x:Null を使うと、そのプロパティに null を設定する意図を明確にできます。
マークアップ拡張の省略形
マークアップ拡張の多くは、省略形で書かれています。
たとえば、次の XAML はよく見かける形です。
<TextBox Text="{Binding UserName}" />
これは、より明示的に書くと次のような意味になります。
<TextBox>
<TextBox.Text>
<Binding Path="UserName" />
</TextBox.Text>
</TextBox>
つまり、{Binding UserName} は、Binding オブジェクトを短く書くための構文です。
同じように、StaticResource もリソース参照を簡潔に書くための構文です。
<Button Style="{StaticResource PrimaryButtonStyle}" />
波かっこで書かれているからといって、まったく別世界の構文ではありません。最終的には、プロパティに設定する値を作っていると考えられます。
波かっこを文字として表示したい場合
属性値として波かっこから始まる文字列を書きたい場合、XAML はマークアップ拡張として解釈しようとします。
そのようなときは、空の波かっこを先頭に付けてエスケープします。
<TextBlock Text="{}{Binding ではなく文字列として表示}" />
このように書くと、後続の波かっこはマークアップ拡張ではなく文字列として扱われます。
頻繁に使う書き方ではありませんが、エラーメッセージやサンプル文字列を表示するときに役立つことがあります。
まとめ
マークアップ拡張は、XAML の属性値で特別な値を作ったり参照したりするための仕組みです。
| マークアップ拡張 | 役割 |
|---|---|
Binding | データとプロパティを結び付ける |
StaticResource | リソースを読み込み時に参照する |
DynamicResource | 実行中のリソース変更を反映できる形で参照する |
x:Static | 静的メンバーを参照する |
x:Null | null を表す |
XAML の構文は、最終的にはほとんどが「オブジェクトを作り、プロパティに値を設定する」ための書き方です。
属性構文は、短い値を設定します。
プロパティ要素構文は、複雑な値を要素として設定します。
添付プロパティは、別の型が管理する追加情報を設定します。
マークアップ拡張は、参照やデータバインディングなどによって値を用意します。
この視点を持つと、XAML は記号の集まりではなく、WPF のオブジェクトを組み立てるための一貫した構文として読めるようになります。