(XAML#07)「スタイルと BasedOn による拡張」
スタイルとは、複数のコントロールで共通する外観を統一しやすくするための機能です。技術的には、外観に関わらずコントロールのプロパティをまとめて設定できるようにする機能です。
スタイルは、リソースとして定義することが多いのですが、これは単独のコントロールに割り当てるだけでは意味がないためで、5日目に紹介した「プロパティ要素構文」を使って次のように定義することができます。
<Button Content="One">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Red" />
</Style>
</Button.Style>
</Button>
これは、次の記述と同じことです。
<Button Content="One" Foreground="Red" />
Style で TargetType を指定したコントロール型(クラス)のプロパティが、Style 中の Setter に指定するプロパティ(Property プロパティ)で利用できます(TargetTypeは、より単純に「TargetType="Button"」と書くこともできます)。
前述のとおり、通常はリソースとして定義します。以下の定義では、ウィンドウ(Window)内で定義されたすべての Button 型のコントロールの文字色(Foreground プロパティ)を "Red" に、スタイル(FontStyleプロパティ)を "Italic" に設定します。
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="325" Width="525">
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontStyle" Value="Italic" />
</Style>
</Window.Resources>
<StackPanel>
<!-- すべてのコントロールにだけスタイルが適用される -->
<Button Content="One" />
<Button Content="Two" />
<Button Content="Three" />
</StackPanel>
</Window>
特定のコントロールにだけ適用したい場合には、スタイルにキー(x:Key)を設定して、コントロールの Style プロパティで明示的に指定します。
<Window.Resources>
<Style x:Key="MyStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontStyle" Value="Italic" />
</Style>
</Window.Resources>
<StackPanel>
<!-- 最初のコントロールにだけスタイルが適用される -->
<Button Content="One" Style="{StaticResource MyStyle}" />
<Button Content="Two" />
<Button Content="Three" />
</StackPanel>
ここでは Window のリソース(Resourcesプロパティ)として定義していますが、3日目に紹介した通り、リソースを定義する場所によって影響範囲を変えることができます。App.xaml の Application で定義すれば、アプリケーション全体にスタイルを適用できます。
さて、一般的なスタイルとして定義した後、さらに個別に異なるスタイルを定義したいことがあります。このような場合には基本的なスタイル定義を BasedOn 属性で参照した上で、設定を追加できます。
<Window.Resources>
<Style x:Key="MyStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontStyle" Value="Italic" />
</Style>
<!-- MyStyle をもとに新たな設定を追加する -->
<Style x:Key="ExtraStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource MyStyle}">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Window.Resources>
<StackPanel Grid.Column="0">
<!-- MyStyle の設定が適用される -->
<Button Content="One" Style="{StaticResource MyStyle}" />
<!-- ExtraStyle の設定が適用される -->
<Button Content="Two" Style="{StaticResource ExtraStyle}" />
<!-- ExtraStyle の設定が適用される(自身で設定した Foreground が最優先) -->
<Button Content="Three" Style="{StaticResource ExtraStyle}" Foreground="Green" />
</StackPanel>
...
あたかもスタイルを“継承”できるような機能です。初期の WPF には BasedOn はありませんでしたが、このように「既存のものを拡張して使う」方がスタイルの管理がしやすくなります。
また、キーを使わずに設定したスタイルを継承する場合には、「BasedOn="{StaticResource {x:TypeButton}}"」のように記述します。以下は、ウィンドウのリソースとして定義したスタイルを、2つの StackPanel で、それぞれ異なるスタイルに置き換えている例です。
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontStyle" Value="Italic" />
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Foreground" Value="Blue" />
</Style>
</StackPanel.Resources>
<Button Content="One" />
<Button Content="Two" />
<Button Content="Three" />
</StackPanel>
<StackPanel Grid.Column="1">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</StackPanel.Resources>
<Button Content="First" />
<Button Content="Second" />
<Button Content="Third" />
</StackPanel>
</Grid>
通常は、アプリケーション全体で適用されるスタイルを App.xaml に定義しておき、ウィンドウごとに個別のスタイルを設定する際に BasedOn を使うようにします。
このようにキーを設定せずに全体に適用するスタイルがあるときでも、特定のコントロールでスタイルを適用したくないならば、そのコントロールの属性として「Style="{x:Null}"」と指定しておきます。これで、そのコントロールのデフォルトの状態が使われるようになります。