Visual Studio 2010 自習書 ~ Do-It-Yourself シリーズ ~
Visual Studio 2010 による Windows アプリケーション開発の基礎
第 7 回 スタイル
更新日: 2010 年 12 月 13 日
ダウンロード (XPS、959 KB | PDF、943 KB)
目次
- スタイル
- コントロールとスタイル
- スタイルの継承
- 汎用的なスタイルの設定
- TargetType のないスタイル
- プロパティ ウィンドウ
- トリガー
- まとめ
WPF アプリケーションは、柔軟性の高いユーザー インターフェイスを作成できる仕組みを持っています。ボタンやチェック ボックスなど、コントロールの形状や表現も多彩です。この能力を、より一貫性のある形で使いやすくするものがスタイルです。
ここでは、次のことを学習します。
- スタイルの基礎
- スタイルとリソース
- スタイルの継承
- トリガー
ページのトップへ
1. スタイル
デザイン性の高いユーザー インターフェイスを作成する時に、さまざまなコントロールの属性 (プロパティ) を設定することがあります。この時、コントロールごとに設定するのではなく、まとめて設定できる方が便利です。これは、WPF デザイナーで複数のコントロールをまとめて設定するということではなく、設定をまとめて保存しておき、その設定を新しく配置するコントロールに適用したり、設定を変更しようとする時に、その設定が適用されているすべてのコントロールが変更されるようにするということです。このような仕組みを提供するのがスタイルです。
たとえば、次のように 1 つのスタイルを複数のコントロールに適用できます。
Visual Studio 2010 でスタイルを使うには第 6 回で説明した XAML を使って記述する必要があります。
ページのトップへ
2. コントロールとスタイル
スタイルは、元々コントロールの属性 (プロパティ) として設定するものです。スタイルを使ったボタンを XAML で記述する例を以下に示します。
<Window x:Class="WpfApplication7.MainWindow"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="240" Width="320">
<Grid>
<Button Content="Button">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="30" />
<Setter Property="Background" Value="LightCyan" />
<Setter Property="BorderBrush" Value="Blue" />
</Style>
</Button.Style>
</Button>
</Grid>
</Window>
スタイル (Style) は、TargetType によって、どんな型に対するプロパティを設定するのかを判断します。また、スタイルに割り当てられた Setter 要素で、どのプロパティ名 (Property) に、どんな値 (Value) を設定するのかを指定します。コントロールは、自分の Style プロパティに割り当てられているスタイルをコントロールの既定値とみなします。もし、スタイルで設定されていたプロパティがあっても、コントロール自身の属性としてプロパティの設定があれば優先されます。
プログラムを実行すると、次のように表示されます。
実際に複数のコントロールにスタイルを適用することを考えると、コントロールごとにスタイルを指定することはありません。ほとんどの場合、スタイルでは第 6 回で説明したリソースを使います。前述のスタイルの定義を、リソースを使って書き直すと次のようになります。
Title="MainWindow" Height="240" Width="320">
<Window.Resources>
<Style x:Key="MyStyle" TargetType="Button">
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="30" />
<Setter Property="Background" Value="LightCyan" />
<Setter Property="BorderBrush" Value="Blue" />
</Style>
</Window.Resources>
<Grid>
<Button Content="Button" Style="{StaticResource MyStyle}" />
</Grid>
</Window>
このようにリソースとして名前 (x:Key) を付けて定義したことで、他のボタンでもスタイルを割り当てやすくなります。たとえば、次のように定義してみましょう。
</Window.Resources>
<StackPanel>
<Button Content="Button1" Style="{StaticResource MyStyle}" />
<Button Content="Button2" Style="{StaticResource MyStyle}" />
<Button Content="Button3" Style="{StaticResource MyStyle}" />
</StackPanel>
</Window></Window>
プログラムを実行した様子を示します。
リソースを定義する場合と同じように、スタイルの Setter で定義する値には、数値や文字列だけでなく、ブラシのような複合的なものも使えます。
前述のスタイルのうち Background の設定を次のように変更します。
<Style x:Key="MyStyle" TargetType="Button">
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="30" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Color="Green" Offset="0.0" />
<GradientStop Color="Yellow" Offset="0.6" />
<GradientStop Color="Lime" Offset="1.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="Blue" />
</Style>
プログラムを実行すると、3 つのボタンの外観が揃って変更されることがわかります。
スタイルの継承
基本的なスタイルを定義したものの、個々のグループごとにスタイルを変更したい場合もあります。そのような場合に、既存のスタイルを使った新しいスタイルを定義できます。既存のスタイルを継承する場合には、スタイルを定義する時に継承元のスタイルを BasedOn 属性で指定します。
前述の例に、新しいスタイル NewStyle と、2 番目のボタンで、このスタイルを利用するように修正する例を示します。
</Style>
<Style x:Key="NewStyle" TargetType="Button"
BasedOn="{StaticResource MyStyle}">
<Setter Property="Width" Value="120" />
<Setter Property="Foreground" Value="Red" />
</Style>
</Window.Resources>
<StackPanel>
<Button Content="Button1" Style="{StaticResource MyStyle}" />
<Button Content="Button2" Style="{StaticResource NewStyle}" />
<Button Content="Button3" Style="{StaticResource MyStyle}" />
</StackPanel>
</Window>
プログラムを実行すると、次のようになります。
スタイルを継承する時には、既存の設定を上書きすることも、新しい設定 (Setter) を追加することもできます。ここでは、MyStyle スタイルで設定していた幅 (Width) を新しく設定しなおし、さらに文字の色 (Foreground) の設定を追加しています。
汎用的なスタイルの設定
ウィンドウ上のすべてのボタンで同じスタイルを使いたいのであれば、わざわざキーを指定する必要はありません。キーが指定されていないスタイル指定は、TargetType に指定された型のすべてのコントロールに作用するようになります。コントロールを定義する際に Style プロパティを指定する必要もありません。
このようなスタイルを使う場合、スタイルを継承するための BasedOn プロパティには、継承元として使われている型を x:Type で指定します。
前述の XAML を、次のように修正します。
…
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="80" />
<Setter Property="Height" Value="30" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Color="Green" Offset="0.0" />
<GradientStop Color="Yellow" Offset="0.6" />
<GradientStop Color="Lime" Offset="1.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="Blue" /> </Style>
<Style x:Key="NewStyle" TargetType="Button"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="120" />
<Setter Property="Foreground" Value="Red" />
</Style>
</Window.Resources>
<StackPanel>
<Button Content="Button1" />
<Button Content="Button2" Style="{StaticResource NewStyle}" />
<Button Content="Button3" />
</StackPanel>
</Window>
ここで 1 番目と 3 番目のボタンは、キーの指定されていないスタイルが適用され、2 番目のボタンには、すべての Button コントロール用に定義されたスタイルを継承した NewStyle を適用しています。したがって、実行結果は先ほどと変わりません。
TargetType のないスタイル
これまで、スタイルが適用される型を TargetType で指定してきましたが、TargetType を指定せずにスタイルを定義することもできます。ただし、TargetType の指定されていないスタイルでは、キー (x:Key) を省略することはできません。
TargetType が指定されていない場合には、スタイルで使う設定 (Setter) のプロパティには、対象となるコントロールの型を付けて指定します。この時、Button や TextBox という個々のコントロールではなく、それぞれの基本となっている Control クラスを指定できます。これにより、異なるコントロールに対して、同じスタイルを割り当てられるようになります。
前述の例を、次のように修正します。
…
<Window.Resources>
<Style x:Key="MyStyle">
<Setter Property="Control.Width" Value="80" />
<Setter Property="Control.Height" Value="30" />
<Setter Property="Control.Background">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Color="Green" Offset="0.0" />
<GradientStop Color="Yellow" Offset="0.6" />
<GradientStop Color="Lime" Offset="1.0" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Control.BorderBrush" Value="Blue" />
</Style>
<Style x:Key="NewStyle"
BasedOn="{StaticResource MyStyle}">
<Setter Property="Control.Width" Value="120" />
<Setter Property="Control.Foreground" Value="Red" />
</Style>
</Window.Resources>
<StackPanel>
<Button Content="Button1" Style="{StaticResource MyStyle}" />
<Button Content="Button2" Style="{StaticResource NewStyle}" />
<Button Content="Button3" Style="{StaticResource MyStyle}" />
<TextBox Text="My TextBox" Style="{StaticResource MyStyle}" />
</StackPanel>
</Window>
ここでは、TargetType のないスタイルを定義する際、個々の設定 (Setter) でプロパティに「Control.」を追加しています。これにより、共通のプロパティを変更できます。ユーザー インターフェイス定義の最後には TextBox を追加して、同じスタイルを割り当てています。
なお、TargetType として「Control」を指定しても同じ効果が得られます。
プロパティ ウィンドウ
スタイルの定義は XAML で記述しなければなりませんが、いったん定義したスタイルはコントロールを選び、プロパティ ウィンドウで選択して指定できます。
プロパティ ウィンドウは、WPF デザイナーで選んだコントロールだけでなく、XAML エディターでカーソル位置にある要素についても、プロパティやイベントの一覧を表示します。その要素が、どのような設定ができるのか確認するためにも使えます。
ページのトップへ
3. トリガー
第 4 回のイベントで、トリガーについて簡単に説明しました。トリガーは、スタイルに設定する属性の一種で、特定の条件に応じてプロパティを変更する仕組みです。トリガーの種類には、プロパティの値に対応するプロパティ トリガー、イベントの発生に対応するイベント トリガー、バインディングしたデータに対応するデータ トリガーがあります。ここでは最初の 2 つについて説明します。
プロパティ トリガー
プロパティ トリガーについての第 4 回の例を、リソースを使って書き直すと次のようになります。
<Window x:Class="WpfApplication7.MainWindow"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="240" Width="320">
<Window.Resources>
<Style x:Key="MyStyle" TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Trigger.Setters>
<Setter Property="Height" Value="40"/>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<Button Width="120" Style="{StaticResource MyStyle}">Button</Button>
</StackPanel>
</Window>
プログラムを実行して、マウス カーソルをボタンに近づけると、ボタンの高さが広がります。
イベント トリガー
イベント トリガーは、第 4 回で学んだイベントを、イベント ハンドラーを使わず、トリガーとして使うものです。イベント トリガーは、プロパティ トリガーやデータ トリガーと異なり、「プロパティやデータの状態」ではなく、「イベントが発生した時点」を利用するため、その一瞬だけプロパティを変更するという使い方ができません。このため、Storyboard を使った「アニメーション」を動作させるという使い方をします。
イベント トリガーを使った記述例を示します。
<Window x:Class="WpfApplication7.MainWindow"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="240" Width="320">
<Window.Resources>
<Style x:Key="MyStyle" TargetType="Button">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="40" Duration="0:0:0.2"
Storyboard.TargetProperty="(Button.Height)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="25" Duration="0:0:0.2"
Storyboard.TargetProperty="(Button.Height)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<Button Width="120" Height="25"
Style="{StaticResource MyStyle}">Button</Button>
</StackPanel>
</Window>
アニメーションとは、指定された時間 (または経過時間) でプロパティの値を連続的に変化させていく仕組みです。ここでは、コントロール上にマウス カーソルが移動した時に発生する MouseEnter と、マウス カーソルが離れた時に発生する MouseLeave イベントについて、それぞれボタンの高さ (Button.Height) プロパティを 40 に広げるか、25 に狭めるアニメーションを割り当てています。両方のイベントを使っているのは、イベント トリガーに「プロパティを戻す」というタイミングがないためです。
プログラムを実行して、ボタンにマウス カーソルを合わせたり、離したりすると、ボタンの高さが短い時間をかけて広がったり、狭まることがわかります。
ページのトップへ
4. まとめ
スタイルを使うことで、凝ったデザインをさまざまなコントロールに適用することが容易になり、アプリケーションの外観を効率的に改善できます。また、ユーザー インターフェイスのための動きは、コード ビハインドを使うことなく、ユーザー インターフェイス側の記述のみで定義できます。
次回は、既存のコントロールの構造そのものを変更できるコントロール テンプレートについて学びます。