次の方法で共有


スタイルとテンプレートとは

Windows Presentation Foundation (WPF) のスタイル設定とテンプレートは、開発者やデザイナーが視覚的に説得力のある効果と製品の一貫した外観を作成できるようにする一連の機能を指します。 アプリの外観をカスタマイズする場合は、アプリ内およびアプリ間での外観のメンテナンスと共有を可能にする強力なスタイルとテンプレート モデルが必要です。 WPF はそのモデルを提供します。

WPF スタイル モデルのもう 1 つの機能は、プレゼンテーションとロジックの分離です。 デザイナーは、開発者が C# または Visual Basic を使用してプログラミング ロジックに取り組むのと同時に、XAML のみを使用してアプリの外観を操作できます。

この概要では、アプリのスタイル設定とテンプレートの側面に重点を置き、データ バインディングの概念については説明しません。 データ バインディングの詳細については、「 データ バインディングの概要」を参照してください。

スタイルとテンプレートを再利用できるようにするためのリソースを理解することが重要です。 リソースの詳細については、「 XAML リソースの概要」を参照してください。

サンプル

この概要で提供されるサンプル コードは、次の図に示す 簡単な写真閲覧アプリケーション に基づいています。

Styled ListView

このシンプルな写真サンプルでは、スタイル設定とテンプレートを使用して、視覚的に説得力のあるユーザー エクスペリエンスを作成します。 このサンプルには、2 つの TextBlock 要素と、画像のリストにバインドされた ListBox コントロールがあります。

完全なサンプルについては、「 スタイル設定とテンプレートのサンプルの概要」を参照してください。

スタイル

Styleは、一連のプロパティ値を複数の要素に適用する便利な方法と考えることができます。 FrameworkElementFrameworkContentElementなど、WindowまたはButtonから派生する任意の要素でスタイルを使用できます。

スタイルを宣言する最も一般的な方法は、XAML ファイルの Resources セクションのリソースです。 スタイルはリソースであるため、すべてのリソースに適用されるのと同じスコープ規則に従います。 簡単に言えば、スタイルを宣言する場所は、スタイルを適用できる場所に影響します。 たとえば、アプリ定義 XAML ファイルのルート要素でスタイルを宣言した場合、スタイルはアプリ内の任意の場所で使用できます。

たとえば、次の XAML コードは、 TextBlockの 2 つのスタイルを宣言します。1 つはすべての TextBlock 要素に自動的に適用され、もう 1 つは明示的に参照する必要があります。

<Window.Resources>
    <!-- .... other 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>
    
    <!--A Style that extends the previous TextBlock 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>

上記で宣言したスタイルの使用例を次に示します。

<StackPanel>
    <TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

スタイル付きテキストブロック

詳細については、「 コントロールのスタイルを作成する」を参照してください。

ControlTemplates

WPF では、コントロールの ControlTemplate によってコントロールの外観が定義されます。 新しい ControlTemplate を定義してコントロールに割り当てることで、コントロールの構造と外観を変更できます。 多くの場合、テンプレートには十分な柔軟性があるため、独自のカスタム コントロールを記述する必要はありません。

各コントロールには、 Control.Template プロパティに割り当てられた既定のテンプレートがあります。 このテンプレートは、コントロールの視覚的な表示をコントロールの機能に接続します。 XAML でテンプレートを定義するため、コードを記述せずにコントロールの外観を変更できます。 各テンプレートは、 Buttonなどの特定のコントロール用に設計されています。

一般的に、テンプレートは XAML ファイルの Resources セクションでリソースとして宣言します。 すべてのリソースと同様に、スコープ規則が適用されます。

コントロール テンプレートは、スタイルよりもはるかに複雑です。 これは、コントロール テンプレートによってコントロール全体の外観が書き換えられますが、スタイルは既存のコントロールにプロパティの変更を適用するだけであるためです。 ただし、コントロールのテンプレートは Control.Template プロパティを設定して適用されるため、スタイルを使用してテンプレートを定義または設定できます。

デザイナーを使用すると、通常、既存のテンプレートのコピーを作成して変更できます。 たとえば、Visual Studio WPF デザイナーで、CheckBox コントロールを選択し、右クリックして [テンプレートの編集] を選択し>コピーを作成します。 このコマンドは、 テンプレートを定義するスタイルを生成します。

<Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
    <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual1}"/>
    <Setter Property="Background" Value="{StaticResource OptionMark.Static.Background1}"/>
    <Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border1}"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type CheckBox}">
                <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Border x:Name="checkBoxBorder" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                        <Grid x:Name="markGrid">
                            <Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph1}" Margin="1" Opacity="0" Stretch="None"/>
                            <Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph1}" Margin="2" Opacity="0"/>
                        </Grid>
                    </Border>
                    <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasContent" Value="true">
                        <Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual1}"/>
                        <Setter Property="Padding" Value="4,-1,0,0"/>

... content removed to save space ...

テンプレートのコピーを編集することは、テンプレートのしくみを学習するための優れた方法です。 新しい空白のテンプレートを作成する代わりに、コピーを編集し、ビジュアル プレゼンテーションのいくつかの側面を変更する方が簡単です。

例については、「 コントロールのテンプレートを作成する」を参照してください。

テンプレートバインディング

前のセクションで定義したテンプレート リソースで TemplateBinding Markup Extension が使用されていることに気付いたかもしれません。 TemplateBindingは、テンプレート シナリオ用のバインディングの最適化された形式であり、{Binding RelativeSource={RelativeSource TemplatedParent}}で構築されたバインディングに似ています。 TemplateBinding は、テンプレートの一部をコントロールのプロパティにバインドする場合に便利です。 たとえば、各コントロールには BorderThickness プロパティがあります。 TemplateBindingを使用して、このコントロール設定の影響を受けるテンプレート内の要素を管理します。

ContentControl と ItemsControl

ContentPresenterControlTemplateContentControlが宣言されている場合、ContentPresenterは自動的にContentTemplateおよびContentプロパティにバインドされます。 同様に、ItemsPresenterControlTemplateにあるItemsControlは、ItemTemplateおよびItemsプロパティに自動的にバインドされます。

DataTemplates

このサンプル アプリには、写真の一覧にバインドされた ListBox コントロールがあります。

<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
         Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>

現在、この ListBox は次のようになります。

テンプレートを適用する前の ListBox

ほとんどのコントロールには何らかの種類のコンテンツがあり、多くの場合、そのコンテンツはバインド先のデータから取得されます。 このサンプルでは、データは写真の一覧です。 WPF では、 DataTemplate を使用してデータの視覚的表現を定義します。 基本的に、 DataTemplate に入力した内容によって、レンダリングされたアプリでのデータの外観が決まります。

このサンプル アプリでは、各カスタム Photo オブジェクトには、イメージのファイル パスを指定する文字列型の Source プロパティがあります。 現在、写真オブジェクトはファイル パスとして表示されます。

public class Photo
{
    public Photo(string path)
    {
        Source = path;
    }

    public string Source { get; }

    public override string ToString() => Source;
}
Public Class Photo
    Sub New(ByVal path As String)
        Source = path
    End Sub

    Public ReadOnly Property Source As String

    Public Overrides Function ToString() As String
        Return Source
    End Function
End Class

写真を画像として表示するには、リソースとして DataTemplate を作成します。

<Window.Resources>
    <!-- .... other 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 プロパティは、TargetTypeStyle プロパティに似ています。 DataTemplateが resources セクションにある場合、DataType プロパティを型に指定し、x:Keyを省略すると、その型が表示されるたびにDataTemplateが適用されます。 DataTemplatex:Key を割り当て、その後、StaticResource プロパティや DataTemplate プロパティなどの ItemTemplate 型を受け取るプロパティに対して ContentTemplate として設定するオプションがあります。

基本的に、上の例のDataTemplateでは、Photo オブジェクトがある場合は常に、Image内でBorderとして表示されるように定義されています。 この DataTemplateでは、アプリは次のようになります。

写真の画像

データ テンプレート モデルには、他の機能があります。 たとえば、HeaderedItemsControlMenuなどのTreeView型を使用して他のコレクションを含むコレクション データを表示する場合、HierarchicalDataTemplateがあります。 別のデータテンプレート機能として、カスタムロジックに基づいて使用するDataTemplateSelectorを選択できるDataTemplateがあります。 詳細については、「 データ テンプレートの概要」を参照してください。この概要では、さまざまなデータ テンプレート機能について詳しく説明します。

トリガー (条件や動作を引き起こすもの)

トリガーは、プロパティ値が変更されたとき、またはイベントが発生したときに、プロパティを設定したり、アニメーションなどのアクションを開始したりします。 StyleControlTemplate、および DataTemplate はすべて、一連のトリガーを含むことができる Triggers プロパティを持っています。 トリガーにはいくつかの種類があります。

プロパティトリガー

プロパティ値を設定する Trigger 、またはプロパティの値に基づいてアクションを開始するは、プロパティ トリガーと呼ばれます。

プロパティ トリガーの使用方法を示すために、選択されていない限り、各 ListBoxItem を部分的に透明にすることができます。 次のスタイルは、OpacityListBoxItem値を0.5に設定します。 ただし、 IsSelected プロパティが trueされると、 Opacity1.0 に設定されます。

<Window.Resources>
    <!-- .... other resources .... -->

    <Style TargetType="ListBoxItem">
        <Setter Property="Opacity" Value="0.5" />
        <Setter Property="MaxHeight" Value="75" />
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Trigger.Setters>
                    <Setter Property="Opacity" Value="1.0" />
                </Trigger.Setters>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

この例では、 Trigger を使用してプロパティ値を設定しますが、 Trigger クラスには、トリガーがアクションを実行できるようにする EnterActions プロパティと ExitActions プロパティも含まれています。

MaxHeightListBoxItem プロパティが 75 に設定されていることに注意してください。 次の図では、3 番目の項目が選択されている項目です。

Styled ListView

EventTriggers とストーリーボード

別の種類のトリガーは、イベントの発生に基づいて一連のアクションを開始する EventTriggerです。 たとえば、次の EventTrigger オブジェクトは、マウス ポインターが ListBoxItem に入ったときに、MaxHeight プロパティが 90 秒間で 0.2 の値にアニメーションすることを指定します。 マウスが項目から離れると、プロパティは 1 秒間に元の値 1 戻ります。 To のアニメーションにはMouseLeave値を指定する必要がありません。 これは、アニメーションが元の値を追跡できるためです。

<Style.Triggers>
    <Trigger Property="IsSelected" Value="True">
        <Trigger.Setters>
            <Setter Property="Opacity" Value="1.0" />
        </Trigger.Setters>
    </Trigger>
    <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>
</Style.Triggers>

詳細については、 ストーリーボードの概要を参照してください。

次の図では、マウスが 3 番目の項目を指しています。

スタイル設定のサンプルのスクリーンショット

MultiTriggers(マルチトリガー)、DataTriggers(データトリガー)、MultiDataTriggers(マルチデータトリガー)

TriggerEventTriggerに加えて、他の種類のトリガーもあります。 MultiTrigger では、複数の条件に基づいてプロパティ値を設定できます。 条件のプロパティがデータ バインドされている場合は、 DataTriggerMultiDataTrigger を使用します。

表示状態

コントロールは常に特定の 状態です。 たとえば、マウスがコントロールの表面上を移動すると、コントロールは MouseOverの共通の状態にあると見なされます。 特定の状態のないコントロールは、共通の Normal 状態にあると見なされます。 状態はグループに分割され、前述の状態は状態グループの CommonStatesの一部です。 ほとんどのコントロールには、 CommonStatesFocusStatesの 2 つの状態グループがあります。 コントロールに適用される各状態グループのうち、コントロールは常に各グループの 1 つの状態 ( CommonStates.MouseOverFocusStates.Unfocusedなど) にあります。 ただし、コントロールは、 CommonStates.NormalCommonStates.Disabledなど、同じグループ内の 2 つの異なる状態にすることはできません。 ほとんどのコントロールが認識して使用する状態の表を次に示します。

ビジュアルステート名 VisualStateGroup 名 説明
Normal CommonStates 既定の状態。
MouseOver CommonStates マウス ポインターはコントロールの上に配置されます。
Pressed CommonStates コントロールが押されます。
Disabled CommonStates コントロールが無効になっています。
Focused FocusStates コントロールにフォーカスがあります。
Unfocused FocusStates コントロールにフォーカスがありません。

コントロール テンプレートのルート要素に System.Windows.VisualStateManager を定義することで、コントロールが特定の状態になったときにアニメーションをトリガーできます。 VisualStateManagerでは、監視するVisualStateGroupVisualStateの組み合わせを宣言します。 コントロールがウォッチ状態になると、 VisualStateManager によって定義されたアニメーションが開始されます。

たとえば、次の XAML コードは、CommonStates.MouseOver の状態を監視し、backgroundElement という名前の要素の塗りつぶし色をアニメーション化します。 コントロールがCommonStates.Normal状態に戻ると、backgroundElementという要素の塗りつぶし色が復元されます。

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                    <ColorAnimation Storyboard.TargetName="backgroundElement"
                                    Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                                    To="{TemplateBinding Background}"
                                    Duration="0:0:0.3"/>
                </VisualState>
                <VisualState Name="MouseOver">
                    <ColorAnimation Storyboard.TargetName="backgroundElement"
                                    Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                                    To="Yellow"
                                    Duration="0:0:0.3"/>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        ...

ストーリーボードの詳細については、「 ストーリーボードの概要」を参照してください。

共有リソースとテーマ

一般的な WPF アプリには、アプリ全体に適用される複数の UI リソースがある場合があります。 この一連のリソースをまとめて、アプリのテーマと見なすことができます。 WPF は、 ResourceDictionary クラスとしてカプセル化されたリソース ディクショナリを使用して、UI リソースをテーマとしてパッケージ化するためのサポートを提供します。

WPF テーマは、WPF が任意の要素のビジュアルをカスタマイズするために公開するスタイルとテンプレートのメカニズムを使用して定義されます。

WPF テーマ リソースは、埋め込みリソース ディクショナリに格納されます。 これらのリソース ディクショナリは、署名されたアセンブリ内に埋め込む必要があり、コード自体と同じアセンブリまたはサイド バイ サイド アセンブリに埋め込むことができます。 PresentationFramework.dllの場合、WPF コントロールを含むアセンブリでは、テーマ リソースは一連のサイド バイ サイド アセンブリにあります。

テーマは、要素のスタイルを検索するときに最後に見る場所になります。 通常、検索はまず、適切なリソースを検索する要素ツリーを調べます。次に、アプリ リソース コレクションを調び、最後にシステムに対してクエリを実行します。 これにより、アプリ開発者は、テーマに到達する前に、ツリーまたはアプリ レベルで任意のオブジェクトのスタイルを再定義できます。

リソース ディクショナリは、複数のアプリ間でテーマを再利用できる個々のファイルとして定義できます。 また、同じ種類のリソースを提供し、値が異なる複数のリソース ディクショナリを定義することで、スワップ可能なテーマを作成することもできます。 アプリのスキンを設計する際には、アプリ レベルでこれらのスタイルやその他のリソースを再定義することをお勧めします。

スタイルやテンプレートなどの一連のリソースをアプリ間で共有するには、XAML ファイルを作成し、ResourceDictionary ファイルへの参照を含むshared.xamlを定義します。

<ResourceDictionary.MergedDictionaries>
  <ResourceDictionary Source="Shared.xaml" />
</ResourceDictionary.MergedDictionaries>

これは、スタイルとブラシ リソースのセットを含むshared.xamlを定義するResourceDictionaryの共有です。これにより、アプリ内のコントロールの外観が一貫しています。

詳細については、「マージされたリソース ディクショナリ を参照してください。

カスタム コントロールのテーマを作成する場合は、「コントロールの作成の概要」の「テーマ レベルでのリソースの定義」セクションを参照してください。

こちらも参照ください