スタイルとテンプレート
Windows Presentation Foundation (WPF) のスタイルとテンプレートは、開発者およびデザイナーが視覚的に魅力のある効果を作成したり、それらの製品の外観に統一感を持たせることができる一連の機能 (スタイル、テンプレート、トリガー、およびストーリーボード) を表します。 開発者およびデザイナーはアプリケーション単位で広範囲に外観をカスタマイズできますが、アプリケーション内およびアプリケーション間の外観の保守や共有のためには強力なスタイルとテンプレート モデルが必要です。 Windows Presentation Foundation (WPF) はそのモデルを備えています。
WPF スタイル モデルのもう 1 つの機能は、プレゼンテーションとロジックの分離です。 つまり、デザイナーは、開発者が C# または Visual Basic を使用してプログラミング ロジックを作成するのと同時に、XAML だけを使用してアプリケーションの外観を作成できます。
ここでは、アプリケーションのスタイルとテンプレートの側面に焦点を当て、データ バインディングの概念については説明しません。 データ バインディングの詳細については、「データ バインディングの概要」を参照してください。
また、スタイルやテンプレートの再利用を実現できるリソースについて理解することも重要です。 リソースの詳細については、「リソースの概要」を参照してください。
このトピックは、次のセクションで構成されています。
- スタイルとテンプレートのサンプル
- スタイルの基本
- データ テンプレート
- コントロール テンプレート
- トリガー
- 共有リソースとテーマ
- 関連トピック
スタイルとテンプレートのサンプル
この概要のコード例は、次の図に示すような単純な写真のサンプルに基づいています。
この単純な写真のサンプルでは、スタイルとテンプレートを使用して、視覚効果に優れたユーザー エクスペリエンスを作成します。 このサンプルでは、イメージの一覧にバインドされた 2 つの TextBlock 要素、および ListBox コントロールを使用しています。 サンプルの完全版については、スタイルとテンプレート サンプルの概要を参照してください。
スタイルの基本
Style は、プロパティ値のセットを複数の要素に適用するための便利な方法と考えることができます。 たとえば、次の TextBlock 要素があり、その外観が既定の外観であるとします。
<TextBlock>My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>
既定の外観は、FontSize や FontFamily などのプロパティを各 TextBlock 要素に直接設定することにより変更できます。 ただし、TextBlock 要素で一部のプロパティを共有する場合は、次に示すように、XAML ファイルの Resources セクションに Style を作成することができます。
<Window.Resources>
...
<!--A Style that affects all TextBlocks-->
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="FontSize" Value="14"/>
</Style>
...
</Window.Resources>
スタイルの TargetType を TextBlock 型に設定すると、ウィンドウ内のすべての TextBlock 要素にスタイルが適用されます。
これで、TextBlock 要素は次のように表示されます。
スタイルの拡張
2 つの TextBlock 要素で、FontFamily や中央揃えの HorizontalAlignment など一部のプロパティ値を共有する可能性がありますが、テキスト "My Pictures" にプロパティを追加する必要もあります。 これを行うには、次に示すように、最初のスタイルに基づく新しいスタイルを作成します。
<Window.Resources>
...
<!--A Style that extends the previous TextBlock Style-->
<!--This is a "named style" with an x:Key of TitleText-->
<Style BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock"
x:Key="TitleText">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Foreground">
<Setter.Value>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.0" Color="#90DDDD" />
<GradientStop Offset="1.0" Color="#5BFFFF" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
...
</Window.Resources>
前のスタイルでは x:Key が指定されています。 スタイルを適用するには、次に示すように、TextBlock の Style プロパティを x:Key 値に設定します。
<TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>
この TextBlock スタイルでは、HorizontalAlignment の値が Center に、FontFamily の値が Comic Sans MS に、FontSize の値が 26 に、Foreground の値が上記の例で示した LinearGradientBrush に設定されています。 この例では、基本スタイルの FontSize 値をオーバーライドしていることに注意してください。 Style に同じプロパティを設定する Setter が複数ある場合は、最後に宣言した Setter が優先されます。
TextBlock 要素は、次のような外観になります。
この TitleText スタイルは、TextBlock 型に対して作成されたスタイルを拡張します。 また、x:Key 値を使用して、x:Key を持つスタイルも拡張できます。 具体的な方法については、BasedOn プロパティで示されている例を参照してください。
TargetType プロパティと x:Key 属性の関係
最初の例に示したように、x:Key でスタイルを割り当てずに TargetType プロパティを TextBlock に設定すると、スタイルはすべての TextBlock 要素に適用されます。 この場合、x:Key は暗黙的に {x:Type TextBlock} に設定されます。 x:Key 値を明示的に {x:Type TextBlock} 以外の値に設定すると、この Style がすべての TextBlock 要素に自動的に適用されなくなります。 代わりに、x:Key 値を使用して、このスタイルを明示的に TextBlock 要素に適用する必要があります。 スタイルがリソース セクション内にあり、スタイルに TargetType プロパティを設定しない場合は、x:Key を指定する必要があります。
x:Key の既定値を設定するだけでなく、TargetType プロパティで、setter のプロパティを適用する型を指定します。 TargetType を指定しない場合は、Property="ClassName.Property" 構文を使用して、Setter オブジェクトのプロパティをクラス名で修飾する必要があります。 たとえば、Property="FontSize" の代わりに、Property を "TextBlock.FontSize" または "Control.FontSize" に設定する必要があります。
また、多くの WPF コントロールは、他の WPF コントロールの組み合わせで構成されます。 特定の型のすべてのコントロールに適用するスタイルを作成すると、予期しない結果になる可能性があります。 たとえば、Window 内の TextBlock 型を対象とするスタイルを作成すると、そのスタイルは、別のコントロール (ListBox など) の一部である TextBlock も含め、ウィンドウ内のすべての TextBlock コントロールに適用されます。
スタイルとリソース
FrameworkElement または FrameworkContentElement から派生する任意の要素に対してスタイルを使用できます。 最も一般的なスタイルの宣言方法は、前の例で示したように、XAML ファイル内の Resources セクションでリソースとして宣言することです。 スタイルはリソースであるため、すべてのリソースに適用されるスコープ規則が適用されます。したがって、スタイルをどこで宣言するかは、スタイルの適用対象に影響します。 たとえば、アプリケーション定義 XAML ファイルのルート要素でスタイルを宣言すると、そのスタイルはアプリケーション内のあらゆる場所で使用できます。 一方、ナビゲーション アプリケーションを作成する場合に、アプリケーションのいずれかの XAML ファイルで宣言したスタイルは、その XAML ファイルでしか使用できません。 リソースのスコープ規則の詳細については、「リソースの概要」を参照してください。
また、スタイルとリソースの詳細については、この概要の「共有リソースとテーマ」を参照してください。
プログラムによるスタイルの設定
プログラムで名前付きのスタイルを要素に割り当てるには、リソース コレクションからスタイルを取得して、要素の Style プロパティに割り当てます。 リソース コレクション内の項目の型は Object です。 したがって、取得したスタイルを Style にキャストしてから Style プロパティに割り当てる必要があります。 たとえば、定義済みの TitleText スタイルを、textblock1 という名前の TextBlock に設定するには、次のコードを実行します。
textblock1.Style = CType(Me.Resources("TitleText"), Style)
textblock1.Style = (Style)(this.Resources["TitleText"]);
スタイルが適用されると、シールされて、変更できません。 既に適用されているスタイルを動的に変更する場合は、新しいスタイルを作成して既存のスタイルを置き換える必要があります。 詳細については、IsSealed プロパティのトピックを参照してください。
カスタム ロジックに基づいて適用するスタイルを選択するオブジェクトを作成できます。 具体的な方法については、StyleSelector クラスで示されている例を参照してください。
バインディング、動的リソース、およびイベント ハンドラー
Setter.Value プロパティを使用して、バインディングのマークアップ拡張機能または DynamicResource のマークアップ拡張機能を指定できます。 詳細については、Setter.Value プロパティの例を参照してください。
ここまでは、setter を使用したプロパティ値の設定についてのみ説明してきました。 スタイルではイベント ハンドラーも指定できます。 詳細については、EventSetter のトピックを参照してください。
データ テンプレート
このサンプル アプリケーションでは、写真のリストにバインドされる ListBox コントロールを使用します。
<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>
ListBox の現在の外観は次のとおりです。
ほとんどのコントロールはある型のコンテンツを使用し、そのコンテンツの多くはバインド先のデータから取得しています。 このサンプルでは、データは写真のリストです。 WPF では、DataTemplate を使用して、データの視覚的表現を定義します。 基本的に、DataTemplate に挿入する内容によって、レンダリングされるアプリケーションでのデータの外観が決まります。
このサンプル アプリケーションでは、各カスタム Photo オブジェクトは、イメージのファイル パスを指定する文字列型の Source プロパティを 1 つ持っています。 現在、写真オブジェクトはファイル パスとして表示されています。
写真をイメージとして表示するには、DataTemplate をリソースとして作成します。
<Window.Resources>
...
<!--DataTemplate to display Photos as images
instead of text strings of Paths-->
<DataTemplate DataType="{x:Type local:Photo}">
<Border Margin="3">
<Image Source="{Binding Source}"/>
</Border>
</DataTemplate>
...
</Window.Resources>
DataType プロパティは、Style の TargetType プロパティと非常に似ています。 DataTemplate がリソース セクション内にある場合に、DataType プロパティをある型に指定し、それに x:Key を割り当てない場合、その型が表示されるたびに DataTemplate が適用されます。 x:Key で DataTemplate を割り当ててから、ItemTemplate プロパティや ContentTemplate プロパティなど、DataTemplate 型を取るプロパティの StaticResource として設定するオプションを常に使用できます。
基本的に、上記の例の DataTemplate は、Photo オブジェクトが存在するときは常に、そのオブジェクトが Border 内の Image として表示されることを定義します。 この DataTemplate を使用すると、この例のアプリケーションの外観は次のようになります。
データ テンプレート モデルには他の機能もあります。 たとえば、Menu や TreeView などの HeaderedItemsControl 型を使用して他のコレクションを格納するコレクション データを表示している場合は、HierarchicalDataTemplate が存在します。 別のデータ テンプレート機能として、カスタム ロジックに基づいて使用する DataTemplate を選択できる DataTemplateSelector があります。 詳細については、「データ テンプレートの概要」を参照してください。このトピックでは、さまざまなデータ テンプレート機能について詳しく説明しています。
コントロール テンプレート
WPF では、コントロールの ControlTemplate で、コントロールの外観を定義します。 コントロールの構造や外観を変更するには、そのコントロールの新しい ControlTemplate を定義します。 多くの場合、この方法には十分な柔軟性があるため、独自のカスタム コントロールを作成する必要がありません。 詳細については、「ControlTemplate の作成による既存のコントロールの外観のカスタマイズ」を参照してください。
トリガー
プロパティ値が変更されたり、イベントが発生すると、トリガーはプロパティを設定するか、アニメーションなどのアクションを開始します。 Style、ControlTemplate、および DataTemplate はすべて、トリガーのセットを含むことができる Triggers プロパティを持っています。 トリガーには、次のようにさまざまな種類があります。
プロパティ トリガー
プロパティ値を設定する、またはプロパティ値に基づいてアクションを開始する Trigger を、プロパティ トリガーと呼びます。
プロパティ トリガーを使用する方法を示すために、各 ListBoxItem を、選択されない場合は部分的に透明にします。 次のスタイルでは、ListBoxItem の Opacity 値を 0.5 に設定しています。 ただし、IsSelected プロパティが true の場合は、Opacity が 1.0 に設定されます。
<Style TargetType="ListBoxItem">
<Setter Property="Opacity" Value="0.5" />
<Setter Property="MaxHeight" Value="75" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Opacity" Value="1.0" />
</Trigger>
...
</Style.Triggers>
</Style>
この例では、Trigger を使用してプロパティ値を設定しますが、Trigger クラスには、トリガーでアクションを実行できる EnterActions プロパティと ExitActions プロパティもあります。
ListBoxItem の MaxHeight プロパティが 75 に設定されることに注意してください。 次の図では、3 つ目の項目が選択された項目です。
EventTrigger とストーリーボード
もう 1 つの型のトリガーは、イベントの発生に基づいて一連のアクションを開始する EventTrigger です。 たとえば、次の EventTrigger オブジェクトは、マウス ポインターが ListBoxItem に入ると、MaxHeight プロパティが 0.2 秒間 90 の値にアニメーション化することを指定します。 マウスが項目から離れると、プロパティは、1 秒間、元の値に戻ります。 MouseLeave アニメーションに対して To 値を指定する必要がない理由に注意してください。 これは、アニメーションが元の値を追跡できるからです。
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.2"
Storyboard.TargetProperty="MaxHeight"
To="90" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:1"
Storyboard.TargetProperty="MaxHeight" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
詳細については、「ストーリーボードの概要」を参照してください。
次の図では、マウスが 3 つ目の項目をポイントしています。
MultiTrigger、DataTrigger、および MultiDataTrigger
トリガーの型は、Trigger および EventTrigger 以外にもあります。 MultiTrigger は、複数の条件に基づいてプロパティ値を設定できるようにします。 条件のプロパティがデータ バインドされている場合は、DataTrigger および MultiDataTrigger を使用します。
共有リソースとテーマ
一般的な Windows Presentation Foundation (WPF) アプリケーションには、アプリケーション全体に適用される複数のユーザー インターフェイス (UI) リソースが含まれる場合があります。 このリソースのセットは、まとめてアプリケーションのテーマと見なすことができます。 Windows Presentation Foundation (WPF) は、ResourceDictionary クラスとしてカプセル化されたリソース ディクショナリを使用して、テーマとしてのユーザー インターフェイス (UI) リソースのパッケージ化をサポートします。
Windows Presentation Foundation (WPF) テーマは、要素のビジュアルをカスタマイズするために Windows Presentation Foundation (WPF) が公開するスタイルとテンプレート機構を使用して定義されます。
Windows Presentation Foundation (WPF) テーマ リソースは、埋め込みのリソース ディクショナリに格納されます。 これらのリソース ディクショナリは、署名付きアセンブリ内に埋め込む必要があります。コードそのものと同じアセンブリに埋め込むことも、side-by-side アセンブリに埋め込むこともできます。 PresentationFramework.dll (Windows Presentation Foundation (WPF) コントロールを含むアセンブリ) では、テーマ リソースが一連の side-by-side アセンブリ内にあります。
テーマは、要素のスタイルを検索するときに最後に検索されます。 通常、検索は、最初に該当のリソースを検索する要素ツリーをウォークしてから、アプリケーション リソース コレクション内を探し、最後にシステムを照会します。 この方法により、アプリケーション開発者は、テーマに到達する前にツリーまたはアプリケーション レベルで任意のオブジェクトのスタイルを再定義できます。
リソース ディクショナリは、複数のアプリケーションで 1 つのテーマを再利用できる個々のファイルとして定義できます。 また、リソースの種類は同じでも値が異なる複数のリソース ディクショナリを定義して、入れ替え可能なテーマを作成することもできます。 アプリケーション レベルでこれらのスタイルまたは他のリソースを再定義する方法は、アプリケーションをスキニングする場合に推奨される方法です。
スタイルとテンプレートを含む一連のリソースをアプリケーション間で共有するためには、XAML ファイルを作成し、ResourceDictionary を定義します。 たとえば、ControlTemplate を使用したスタイル設定のサンプルの一部を示す次の図を見てください。
サンプル内の XAML ファイルを見ると、すべてのファイルに次のコードが含まれていることがわかります。
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Shared.xaml" />
</ResourceDictionary.MergedDictionaries>
これは、サンプル内のコントロールを一貫性のある外観にするための一連のスタイル リソースやブラシ リソースを含む ResourceDictionary を定義する shared.xaml の共有です。
詳細については、「マージされたリソース ディクショナリ」を参照してください。
カスタム コントロールのテーマを作成する場合は、「コントロールの作成の概要」の外部のコントロール ライブラリに関するセクションを参照してください。
参照
処理手順
方法 : ControlTemplate によって生成された要素を検索する
方法 : DataTemplate によって生成された要素を検索する