第4回 WPFの「リソース、スタイル、テンプレート」を習得しよう:連載:WPF入門(3/3 ページ)
WPFはコントロールの外観を自由自在にカスタマイズできる柔軟性を備えている。これを実現する仕組みであるリソース、スタイル、テンプレートを解説する。
■コントロール・テンプレート
スタイルを用いることで、コントロールなどの外観をカスタマイズできるが、前節で説明したような単純なプロパティ値の変更だけでは、カスタマイズ可能な範囲は極めて限られている。背景色などの変更程度であれば、従来のGUI作成フレームワークでもある程度可能な範囲である。WPFのスタイルには、トリガーを用いた「状態の変化に応じたスタイル適用」などの先進的な機能もあるものの、まだ驚く範囲ではないだろう。
Windowsフォームなど、従来のGUI作成フレームワークでは、出来合いのコントロールを用いることで高い生産性を得ていたが、その半面、カスタマイズ性は非常に限られていた。結果として、大幅なカスタマイズが必要な場合には、新たなコントロールを1から自作する必要があった。この場合、外観の新規作成だけでなく、例えば「ボタンのクリック」や「テキストの選択」などの機能面まで含めて作り直す必要があり、非常に負荷の高い作業となる。
これに対して、WPFでは、コントロール・テンプレートという機能を用いることで、「ボタンのクリック」などの機能を残したまま、外観だけを任意に変更可能である。
●コントロール・テンプレートの利用
まず、最低限のコントロール・テンプレートを見てみよう。
List 13に、ボタンの外観を水色のだ円形に変えるテンプレートを示す。ボタンなどのコントロールの基底となるControlクラス(System.Windows.Controls名前空間)はTemplateというプロパティを持っていて、このTemplateプロパティにControlTemplateクラス(System.Windows.Controls名前空間)のインスタンスを設定すればよい。
<Button>
<Button.Template>
<ControlTemplate TargetType="Button">
<Ellipse Fill="LightBlue" Width="80" Height="30"/>
</ControlTemplate>
</Button.Template>
</Button>
この程度の例であれば、単純にだ円(=<Ellipse>要素)にMouse.MouseDownイベントを追加した方が似たようなことを手早く実現できるかもしれない。ただ、Buttonクラスなどのコントロールの場合、次回以降で説明する「コマンド」という仕組みも持っているため、これを利用したければ、やはりコントロール・テンプレートを使うことになるだろう。
Buttonクラスなどのコントロールや、Ellipseクラスなどの「シェイプ」に関する詳細は次回以降で説明する。
●コントロール・テンプレートのリソース化と自動適用
もちろん、コントロール・テンプレートもリソース化可能である。List 14に、コントロール・テンプレートをリソース化する例を示す。
<StackPanel>
<StackPanel.Resources>
<ControlTemplate x:Key="MyButtonTemplate" TargetType="Button">
<Ellipse Fill="LightBlue" Width="80" Height="30"/>
</ControlTemplate>
</StackPanel.Resources>
<Button Template="{StaticResource MyButtonTemplate}" />
</StackPanel>
また、スタイルと組み合わせることで、コントロール・テンプレートの自動適用も可能だ。List 15にその例を示す。
<StackPanel>
<StackPanel.Resources>
<!-- 自動適用スタイル -->
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Ellipse Fill="LightBlue" Width="80" Height="30"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<!-- 以下のボタンには、
自動的にコントロール・テンプレートが設定される -->
<Button />
</StackPanel>
●ContentPresenterとTemplateBinding
それでは、もう少し複雑な例を見てみよう。
前述の最低限の例では、<Button>要素のContentプロパティを設定してもボタンの中身が表示されなくなってしまうという問題がある。また、だ円形の色も水色に固定されてしまっている。そこで、List 16に示すような修正が必要となる。
<StackPanel Width="80">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Fill="{TemplateBinding Background}"/>
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<Button Content="ボタン1" Background="LightBlue" Height="30" />
<Button Content="ボタン2" Background="LightPink" Height="30" />
</StackPanel>
以下の2つがポイントとなる。
- <ContentPresenter>要素: この要素が置かれた位置にコントロールの中身(=Contentプロパティに与えた値)が配置される。
- TemplateBindingマークアップ拡張: コントロール・テンプレートの適用先のコントロールに与えられたプロパティ値を取得するために利用する。
上記のコード例では、<ContentPresenter>要素により、<Grid>要素内の水平/垂直方向の中央に<Button>要素のContentプロパティの値(具体的には「ボタン1」と「ボタン2」)が表示され、さらにTemplateBindingマークアップ拡張によって、コントロール・テンプレートの適用先である<Button>要素のBackgroundプロパティの値(具体的には「LightBlue」と「LightPink」)が取得されて用いられる。Figure 7に、その表示結果を示す。
●ルーティング・コマンド
スクロールバーのように、挙動が少し複雑なコントロールに対してコントロール・テンプレートを適用する場合、「ルーティング・コマンド」という仕組みを利用することになる。ルーティング・コマンドの詳細については次回以降で説明することになるが、簡単にいうと、「ページ・アップ」や「端までスクロール」などといった操作が発生したことを親要素に伝達するための仕組みである。
ここでは参考程度のコード提示にとどめるが、ScrollBarクラスに対してコントロール・テンプレートを適用する例をList 17に示す。
<Grid Width="150" Height="150">
<Grid.Resources>
<ControlTemplate x:Key="VerticalScrollBarTemplate"
TargetType="ScrollBar">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="15"/>
<RowDefinition Height="*"/>
<RowDefinition Height="15"/>
</Grid.RowDefinitions>
<RepeatButton Command="ScrollBar.LineUpCommand"
Background="LightBlue" />
<Track Grid.Row="1" IsDirectionReversed="True">
<Track.DecreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageUpCommand"
Background="Red" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Background="Blue" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Command="ScrollBar.PageDownCommand"
Background="Green" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton Command="ScrollBar.LineDownCommand"
Background="LightPink" Grid.Row="2" />
</Grid>
</ControlTemplate>
<Style TargetType="ScrollBar">
<Style.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Template"
Value="{StaticResource VerticalScrollBarTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Visible">
<Ellipse Width="500" Height="500" Fill="Gray" />
</ScrollViewer>
</Grid>
●WPF Themes(WPFテーマ)
コントロール・テンプレートの仕組みは高機能で、柔軟なカスタマイズが可能ではあるが、これをすべてのコントロールに対して1から定義するのは非常に大変な作業となる。自作ではなく、出来合いのものをどこかから探してきて利用する方が現実的かもしれない。
ありがたいことに、CodeplexにおいてフリーのWPFテーマが公開されている。コントロール・テンプレートの利用の際には参考にしてみるのもいいだろう。
次回はデータ・バインディングの仕組みや具体的な用途について説明を行っていく。
「連載:WPF入門」
Copyright© Digital Advantage Corp. All Rights Reserved.