XAML スタイル

XAML フレームワークを使って、さまざまな方法でアプリの外観をカスタマイズできます。 スタイルを使うと、コントロールのプロパティに値を設定し、その設定を再利用することで、複数のコントロールの外観を統一できます。

WinUI とスタイル

WinUI 2.2以降では、WinUI を使用して、UI コンポーネント全体で新しいビジュアル スタイルの更新を提供してきました。 UI が最新のスタイルに更新されないことに気付いた場合は、最新の WinUI NuGet パッケージに更新してください。

WinUI 2.6 以降では、ほとんどのコントロールに新しいスタイルが導入され、また、必要に応じて以前のコントロール スタイルに戻すことができる新しいバージョン管理システムが採用されています。 新しいスタイルの方が Windows の設計方針とも相性が良いため、Microsoft としては、そちらを使用することを推奨しています。 ただし、新しいスタイルが実際のシナリオにそぐわない場合は、以前のバージョンを引き続き利用することができます。

スタイルのバージョンは、WinUI バージョン 2 を使用するときに Application.Resources に追加した XamlControlsResourcesControlsResourcesVersion プロパティを選択することで変更できます。 ControlsResourcesVersion の既定値は列挙型の値 Version2 です。

この値を Version1 に設定すると、最新の WinUI バージョンで使用される新しいスタイルではなく、以前のスタイル バージョンが XamlControlsResources によって読み込まれます。 このプロパティを実行時に変更することはサポートされず、Visual Studio のホット リロード機能は正しく機能しませんが、アプリケーションのリビルド後は、コントロール スタイルの変更が反映されます。

<Application.Resources>
    <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" 
                           ControlsResourcesVersion="Version1"/>
</Application.Resources>

スタイルの基本

スタイルを使用して、再利用可能なリソースにビジュアル プロパティ設定を抽出します。 BorderBrushBorderThickness および Foreground プロパティを設定するスタイルを持つ 3 つのボタンを示す例を次に示します。 スタイルを適用すると、各コントロールでこれらのプロパティを個別に設定しなくても、コントロールを同じように表示できます。

スタイルを適用した 3 つのボタンが横に並べて配置されているスクリーンショット。

XAML でコントロールのスタイルをインラインで定義することも、再利用可能なリソースとして定義することもできます。 個々のページの XAML ファイル、App.xaml ファイル、または別のリソース ディクショナリ XAML ファイルでリソースを定義します。 リソース ディクショナリ XAML ファイルはアプリ間で共有でき、1 つのアプリで複数のリソース ディクショナリをマージできます。 リソースが定義されている場所によって、使用できるスコープが決まります。 ページレベルのリソースは、定義されているページでのみ使用できます。 App.xaml とページの両方で同じキーを持つリソースが定義されている場合、ページ内のリソースは App.xaml のリソースをオーバーライドします。 リソースが別個のリソース ディクショナリ ファイルで定義されている場合、そのスコープはリソース ディクショナリが参照される場所によって決まります。

スタイル定義では、TargetType 属性と 1 つ以上の Setter 要素のコレクションが必要です。 TargetType 属性は、スタイルを適用する FrameworkElement 型を指定する文字列です。 TargetType 値には、Windows Runtime によって定義される FrameworkElement 派生型、または参照されるアセンブリで使用できるカスタム型を指定する必要があります。 コントロールにスタイルを適用しようとして、コントロールの型が適用しようとしているスタイルの TargetType 属性と一致しない場合は、例外が発生します。

Setter 要素には、プロパティが必要です。 これらのプロパティ設定は、設定が適用されるコントロール プロパティと、そのプロパティに設定する値を示します。 Setter.Value は、属性またはプロパティ要素の構文を使用して設定できます。 ここでの XAML は、前に示したボタンに適用されたスタイルを示しています。 この XAML では、最初の 2 つの Setter 要素は属性構文を使用しますが、BorderBrush プロパティの最後の Setter ではプロパティ要素の構文を使用します。 この例では x:Key 属性を使用しないため、スタイルは暗黙的にボタンに適用されます。 スタイルを暗黙的または明示的に適用する方法については、次のセクションで説明します。

<Page.Resources>
    <Style TargetType="Button">
        <Setter Property="BorderThickness" Value="5" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="BorderBrush" >
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="Yellow" Offset="0.0" />
                    <GradientStop Color="Red" Offset="0.25" />
                    <GradientStop Color="Blue" Offset="0.75" />
                    <GradientStop Color="LimeGreen" Offset="1.0" />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

<StackPanel Orientation="Horizontal">
    <Button Content="Button"/>
    <Button Content="Button"/>
    <Button Content="Button"/>
</StackPanel>

暗黙的または明示的なスタイルを適用する

スタイルをリソースとして定義する場合、コントロールに適用する方法は 2 つあります。

スタイルに x:Key 属性が含まれている場合は、コントロールの Style プロパティをキー付きスタイルに設定することによってのみ、その属性をコントロールに適用できます。 これに対し、x:Key 属性を持たないスタイルは、ターゲット型のすべてのコントロールに自動的に適用され、それ以外の場合は明示的なスタイル設定はありません。

暗黙的なスタイルと明示的なスタイルを示す 2 つのボタンを次に示します。

暗黙的および明示的にスタイル設定されたボタン。

この例では、最初のスタイルには x:Key 属性が含まれており、ターゲットとなる型は Button です。 最初のボタンの Style プロパティはこのキーに設定されているため、このスタイル明示的に適用されます。 2 番目のスタイルは、ターゲットとなる型が Button で、スタイルに x:Key 属性が含まれないため、2 番目のボタンに明示的に適用されます。

<Page.Resources>
    <Style x:Key="PurpleStyle" TargetType="Button">
        <Setter Property="FontFamily" Value="Segoe UI"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Foreground" Value="Purple"/>
    </Style>

    <Style TargetType="Button">
        <Setter Property="FontFamily" Value="Segoe UI"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="RenderTransform">
            <Setter.Value>
                <RotateTransform Angle="25"/>
            </Setter.Value>
        </Setter>
        <Setter Property="BorderBrush" Value="Green"/>
        <Setter Property="BorderThickness" Value="2"/>
        <Setter Property="Foreground" Value="Green"/>
    </Style>
</Page.Resources>

<Grid x:Name="LayoutRoot">
    <Button Content="Button" Style="{StaticResource PurpleStyle}"/>
    <Button Content="Button"/>
</Grid>

ベースのスタイルを使用する

スタイルを維持しやすくし、スタイルの再利用を最適化するには、他のスタイルから継承するスタイルを作成できます。 BasedOn プロパティを使用して、継承されたスタイルを作成します。 他のスタイルから継承するスタイルは、同じ型のコントロール、または基本スタイルが対象とする型から派生したコントロールを対象とする必要があります。 たとえば、基本スタイルが ContentControl を対象としている場合、このスタイルに基づくスタイルは ContentControl または ContentControl から派生する型 (Button ScrollViewer など) をターゲットにすることができます。 基になっているスタイルで値が設定されていない場合は、基本スタイルから継承されます。 基本スタイルから値を変更するには、基になっているスタイルによってその値がオーバーライドされます。 次の例では、同じ基本スタイルを継承するスタイルを持つ ButtonCheckBox を示します。

ベースのスタイルを使用したスタイル付きのボタン。

基本スタイルは ContentControl を対象とし、Height プロパティと Width プロパティを設定します。 このスタイルに基づくスタイルは、ContentControl から派生した CheckBoxButton を対象とします。 基になっているスタイルは、BorderBrush プロパティと Foreground プロパティに異なる色を設定します。 (通常、境界線は CheckBox 周囲に配置されません。ここではスタイルの効果を示すために配置します)。

<Page.Resources>
    <Style x:Key="BasicStyle" TargetType="ContentControl">
        <Setter Property="Width" Value="130" />
        <Setter Property="Height" Value="30" />
    </Style>

    <Style x:Key="ButtonStyle" TargetType="Button"
           BasedOn="{StaticResource BasicStyle}">
        <Setter Property="BorderBrush" Value="Orange" />
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="Foreground" Value="Red" />
    </Style>

    <Style x:Key="CheckBoxStyle" TargetType="CheckBox"
           BasedOn="{StaticResource BasicStyle}">
        <Setter Property="BorderBrush" Value="Blue" />
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="Foreground" Value="Green" />
    </Style>
</Page.Resources>

<StackPanel>
    <Button Content="Button" Style="{StaticResource ButtonStyle}" Margin="0,10"/>
    <CheckBox Content="CheckBox" Style="{StaticResource CheckBoxStyle}"/>
</StackPanel>

ツールを使用してスタイルを簡単に操作する

コントロールにスタイルを簡単に適用するには、Microsoft Visual Studio XAML デザイン サーフェイスでコントロールを右クリックし、[スタイルの編集] または [テンプレートの編集] を選択します (右クリックするコントロールに応じて)。 その後、[リソースの適用] を選択して既存のスタイルを適用するか、[空の作成] を選択して新しいスタイルを定義できます。 空のスタイルを作成する場合は、ページ、App.xaml ファイル、または別のリソース ディクショナリで定義するオプションが表示されます。

ライトウェイト スタイル

システム ブラシのオーバーライドは通常、アプリ レベルまたはページ レベルで行われます。どちらの場合も、色のオーバーライドは、そのブラシを参照するすべてのコントロールに影響します。XAML では、多くのコントロールが同じシステム ブラシを参照できます。

2 つのボタンのスクリーンショット: 1 つは静止状態で、もう 1 つは軽量なスタイル設定が適用されています。

<Page.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Light">
                 <SolidColorBrush x:Key="ButtonBackground" Color="Transparent"/>
                 <SolidColorBrush x:Key="ButtonForeground" Color="MediumSlateBlue"/>
                 <SolidColorBrush x:Key="ButtonBorderBrush" Color="MediumSlateBlue"/>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
</Page.Resources>

PointerOver (マウスがボタンの上にマウス ポインターを置く)、PointerPressed (ボタンが呼び出される)、または無効 (ボタンが対話できない) などの状態の場合。 これらの終わりは、元のライトウェイト スタイル名 (ButtonBackgroundPointerOverButtonForegroundPressedButtonBorderBrushDisabled など) に追加されます。これらのブラシも変更すると、コントロールがアプリのテーマに一貫して色付けされます。

これらのブラシ オーバーライドを App.Resources レベルに配置すると、1 ページではなく、アプリ全体のすべてのボタンが変更されます。

コントロールごとのスタイル設定

その他の場合は、1 ページ上の 1 つのコントロールを変更するだけで、そのコントロールの他のバージョンを変更せずに、特定の方法で表示する必要があります。

スタイルを適用された 3 つのボタンを上下に積み重ねて配列したスクリーンショット。

<CheckBox Content="Normal CheckBox" Margin="5"/>
<CheckBox Content="Special CheckBox" Margin="5">
    <CheckBox.Resources>
        <ResourceDictionary>
            <ResourceDictionary.ThemeDictionaries>
                <ResourceDictionary x:Key="Light">
                    <SolidColorBrush x:Key="CheckBoxForegroundUnchecked"
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxForegroundChecked"
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxCheckGlyphForegroundChecked"
                        Color="White"/>
                    <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeChecked"  
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxCheckBackgroundFillChecked"
                        Color="Purple"/>
                </ResourceDictionary>
            </ResourceDictionary.ThemeDictionaries>
        </ResourceDictionary>
    </CheckBox.Resources>
</CheckBox>
<CheckBox Content="Normal CheckBox" Margin="5"/>

これは、そのコントロールが存在していたページ上の 1 つの "Special CheckBox" にのみ影響します。

カスタム コントロール

Microsoft の組み込みのコントロールと視覚的または機能的に調和したカスタム コントロールを独自に作成するときは、暗黙的なスタイル設定リソースと軽量スタイル設定リソースを使用してカスタム コンテンツを定義することを検討してください。 リソースを直接使用するか、またはリソースの別名を新たに作成することができます。

コントロール リソースを直接使用する

たとえば、ボタンに似たコントロールを作成する場合、次のように、コントロールにボタン リソースを直接参照させることができます。

<Style TargetType="local:MyCustomControl">
  <Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
  <Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
</Style>

コントロール リソースの別名を新たに作成する

また、独自のリソースを作成したい場合は、それらのカスタム名を、Microsoft の既定の軽量スタイル設定リソースに対する別名として設定する必要があります。

たとえば、カスタム コントロールのスタイルに、特殊なリソース定義を設定するとしましょう。

<Style TargetType="local:MyCustomControl">
  <Setter Property="Background" Value="{ThemeResource MyCustomControlBackground}" />
  <Setter Property="BorderBrush" Value="{ThemeResource MyCustomControlBorderBrush}"/>
</Style>

この場合、リソース ディクショナリまたはメイン定義内で、軽量スタイル設定リソースをカスタム リソースにフックすることになります。

<ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Default">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>        
    <ResourceDictionary x:Key="Light">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

3 種類のテーマ変更 (DefaultLightHighContrast) を適切に処理するためには、ThemeDictionary を 3 回複製して使用する必要があります。

注意事項

軽量スタイル設定リソースを新しい別名に割り当てると同時に、軽量スタイル設定リソースを再定義する場合、リソース ルックアップが正しい順序で行われないと、カスタマイズが適用されないことがあります。 たとえば、MyCustomControlBackground が見つかる前に検索される場所で ButtonBackground をオーバーライドした場合、そのオーバーライドは適用されません。

コントロールの再スタイル設定を避ける

WinUI 2.2以降には、WinUI コントロールとシステム コントロールの両方の新しいスタイルとテンプレートが含まれています。

Microsoft が提供する最新の視覚スタイルに追随する一番の方法は、最新の WinUI 2 パッケージを使用し、カスタムのスタイルとテンプレート (再テンプレート化ともいいます) を避けることです。 それでもスタイルは、アプリ内のコントロール全体に一貫した方法で一連の値を適用する有効な手段です。 使用する際は、必ず最新のスタイルをベースにするようにしてください。

WinUI スタイル (Windows.UI.Xaml.Controls 名前空間) を使用するシステム コントロールの場合、BasedOn="{StaticResource Default<ControlName>Style}" を設定します (<ControlName> はコントロールの名前)。 次に例を示します。

<Style TargetType="TextBox" BasedOn="{StaticResource DefaultTextBoxStyle}">
    <Setter Property="Foreground" Value="Blue"/>
</Style>

WinUI 2 コントロール (Microsoft.UI.Xaml.Controls 名前空間) では、既定のスタイルがメタデータに定義されるため、BasedOn は省略します。

派生コントロール

既存の XAML コントロールからカスタム コントロールを派生させた場合、WinUI 2 スタイルが既定では適用されません。 WinUI 2 スタイルを適用するには、次のようにします。

  • TargetType がカスタム コントロールに設定された新しいスタイルを作成します。
  • そのスタイルのベースとして、派生元のコントロールの既定のスタイルを使用します。

この場合の一般的なシナリオの 1 つは、ContentDialog から新しいコントロールを派生させることです。 この例は、DefaultContentDialogStyle をカスタム ダイアログに適用する新しいスタイルを作成する方法を示しています。

<ContentDialog
    x:Class="ExampleApp.SignInContentDialog"
    ... >

    <ContentDialog.Resources>
        <Style TargetType="local:SignInContentDialog" BasedOn="{StaticResource DefaultContentDialogStyle}"/>
        ...
    </ContentDialog.Resources> 
    <!-- CONTENT -->
</ContentDialog>        

template プロパティ

スタイル セッターは、コントロールTemplate プロパティに使用できます。実際、これは一般的な XAML スタイルとアプリの XAML リソースの大部分を構成します。 これについては、「コントロール テンプレート」のトピックで詳述されています。