XAML テーマ リソース

XAML のテーマ リソースは、アクティブなシステム テーマに応じて異なる値を適用するリソースのセットです。 XAML フレームワークでは、3 つのテーマがサポートされています。"Light"、"Dark"、および "HighContrast" です。

前提条件:このトピックでは、既に「ResourceDictionary と XAML リソースの参照」を読んでいることを前提としています。

テーマ リソースと静的リソース

既存の XAML リソース ディクショナリから XAML リソースを参照できる XAML マークアップ拡張には、{StaticResource} マークアップ拡張{ThemeResource} マークアップ拡張の 2 つがあります。

{ThemeResource} マークアップ拡張はアプリの読み込み時に評価され、その後、実行時にテーマが変更されたときにもそのつど評価されます。 これは通常、ユーザーがデバイスの設定を変更した場合、またはアプリ内でプログラムによってアプリの現在のテーマが変更された場合に発生します。

これに対して {StaticResource} マークアップ拡張は、XAML が最初にアプリに読み込まれるときにのみ評価されます。 これが更新されることはありません。 これは、アプリの起動時に XAML を検索し、実際のランタイム値に置き換えるようなものです。

リソース ディクショナリ構造でのテーマ リソース

各テーマ リソースは、XAML ファイル themeresources.xaml の一部です。 設計の目的には、Windows ソフトウェア開発キット (Windows SDK) のインストール先の \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<SDK version>\Generic フォルダーにある themeresources.xaml を使うことができます。 themeresources.xaml 内のリソース ディクショナリは、同じディレクトリの generic.xaml にも複製されています。

これらの物理ファイルは Windows ランタイムでランタイム検索に使われません。 そのため、明示的に DesignTime フォルダー内にあり、既定ではアプリにコピーされません。 代わりに、これらのリソース ディクショナリは Windows ランタイム自体の一部としてメモリ内に存在し、テーマ リソース (またはシステム リソース) へのアプリの XAML リソース参照は実行時にメモリ内で解決されます。

カスタム テーマ リソースのガイドライン

独自のカスタム テーマ リソースを定義して使うときは、次のガイドラインに従ってください。

  • "HighContrast" ディクショナリに加えて、"Light" と "Dark" の両方のテーマ ディクショナリを指定します。 "Default" をキーとする ResourceDictionary を作成することもできますが、明示的に "Light"、"Dark"、"HighContrast" を使うことをお勧めします。

  • {ThemeResource} マークアップ拡張を次で使用します:スタイル、セッター、コントロール テンプレート、プロパティ セッター、アニメーション。

  • ThemeDictionaries 内のリソース定義では、{ThemeResource} マークアップ拡張 を使わないでください。 代わりに、{StaticResource} マークアップ拡張を使います。

    例外:ThemeDictionaries 内のアプリ テーマに依存しないリソースを参照するために {ThemeResource} マークアップ拡張を使うことができます。 このようなリソースの例として、SystemAccentColor などのアクセント カラー リソースや、通常は "SystemColor" というプレフィックスの付いた SystemColorButtonFaceColor などのシステム カラー リソースがあります。

注意事項

これらのガイドラインに従わないと、テーマに関連する予期しない動作がアプリで発生することがあります。 詳しくは、「テーマ リソースのトラブルシューティング」セクションをご覧ください。

XAML 色見本とテーマ依存のブラシ

XAML における Windows 色見本は、"Light"、"Dark"、"HighContrast" の各テーマの色のセットを組み合わせることで構成されます。 システム テーマを変更する場合でも、テーマを独自の XAML 要素に適用する場合でも、カラー リソースがどのように構成されるかを理解することが重要です。

Windows アプリで色を適用する方法について詳しくは、Windows アプリでの色使いに関する記事をご覧ください。

Light テーマと Dark テーマの色

XAML フレームワークには、"Light" と "Dark" のテーマに合わせてカスタマイズされた名前付きの Color リソースのセットが用意されています。 WinUI 2 の場合、テーマ リソースは、共通テーマ リソース Xaml ファイルで定義されます。 色の名前はその使用目的を示すものであり、すべての Color リソースに対応する SolidColorBrush リソースがあります。

ヒント

これらの色の視覚的な概要については、WinUI 3 ギャラリー アプリの を参照してください。

WinUI 3 ギャラリー アプリには、ほとんどの WinUI 3 コントロールと機能の対話型の例が含まれています。 Microsoft Store からアプリを入手するか、GitHub でソース コードを取得します。

Windows システムのコントラスト テーマの色

XAML フレームワークによって提供されるリソースのセットのほかに、Windows のシステム パレットから派生するカラー値のセットがあります。 これらの色は、Windows ランタイムや Windows アプリに固有のものではありません。 しかし、"HighContrast" テーマでシステムが動作しているとき (およびアプリが実行されているとき) には、XAML Brush リソースの多くでこれらの色が使われます。 XAML フレームワークには、このようなシステム全体の色がキーを持つリソースとして用意されています。 これらのキーは、SystemColor[name]Color という名前付け形式に従います。

コントラスト テーマのサポートの詳細については、「コントラスト テーマ」を参照してください。

システムのアクセント カラー

システムのコントラスト テーマの色に加えて、システムのアクセント カラーも、SystemAccentColor というキーを使う特別なカラー リソースとして用意されています。 このリソースは、ユーザーが Windows の個人設定でアクセント カラーとして指定した色を実行時に取得します。

注意

システム カラー リソースをオーバーライドすることもできますが、特にコントラスト テーマの設定については、ユーザーによる色の選択を尊重することをお勧めします。

テーマ依存のブラシ

システム テーマ リソース ディクショナリの SolidColorBrush リソースの Color プロパティは、前のセクションに示したカラー リソースを使って設定されます。 XAML 要素に色を適用するには、ブラシ リソースが使われます。

このブラシの色の値が実行時にどのように決定されるかを見てみましょう。 "Light" と "Dark" の各リソース ディクショナリでは、このブラシは次のように定義されています。

<SolidColorBrush x:Key="TextFillColorPrimaryBrush" Color="{StaticResource TextFillColorPrimary}"/>

"HighContrast" リソース ディクショナリでは、このブラシは次のように定義されています。

<SolidColorBrush x:Key="TextFillColorPrimaryBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>

このブラシが XAML 要素に適用されるとき、その色は、次の表に示すように現在のテーマによって実行時に決定されます。

Theme カラー リソース ランタイム値
淡色 TextFillColorPrimary #E4000000
TextFillColorPrimary #FFFFFFFF
HighContrast SystemColorWindowTextColor Text の設定で指定された色。

XAML の書体見本

themeresources.xaml ファイルには、UI 上のテキスト コンテナー (具体的には TextBlock または RichTextBlock) に適用できる Style を定義するリソースがいくつか定義されています。 これらは、既定の暗黙的なスタイルとは異なります。 これらのスタイルを使うと、「フォントのガイドライン」で説明されている Windows の書体見本に一致する XAML UI 定義を簡単に作成できるようになります。

これらのスタイルは、テキスト コンテナー全体に適用されるテキスト属性を設定するものです。 テキストの一部にのみスタイルを適用する場合は、コンテナー内のテキスト要素に属性を設定します。たとえば、TextBlock.InlinesRunRichTextBlock.BlocksParagraph を使うことができます。

スタイルを TextBlock に適用すると、次のようになります。

text block styles

スタイル Weight サイズ
Caption 通常 12
本文 通常 14
Body Strong 中太字 14
Body Large 通常 18
サブタイトル 中太字 20
タイトル 中太字 28
Title Large 中太字 40
表示 中太字 68
<TextBlock Text="Caption" Style="{StaticResource CaptionTextBlockStyle}"/>
<TextBlock Text="Body" Style="{StaticResource BodyTextBlockStyle}"/>
<TextBlock Text="Body Strong" Style="{StaticResource BodyStrongTextBlockStyle}"/>
<TextBlock Text="Body Large" Style="{StaticResource BodyLargeTextBlockStyle}"/>
<TextBlock Text="Subtitle" Style="{StaticResource SubtitleTextBlockStyle}"/>
<TextBlock Text="Title" Style="{StaticResource TitleTextBlockStyle}"/>
<TextBlock Text="Title Large" Style="{StaticResource TitleLargeTextBlockStyle}"/>
<TextBlock Text="Display" Style="{StaticResource DisplayTextBlockStyle}"/>

アプリで Windows 書体見本を使用する方法のガイダンスについては、「Windows アプリの文字体裁」をご覧ください。

XAML スタイルの詳細については、GitHub の WinUI に関するページを参照してください。

ヒント

これらのスタイルのビジュアルの概要については、WinUI 3 ギャラリー アプリの 文字体裁に関するページを参照してください。

BaseRichTextBlockStyle

TargetType:RichTextBlock

他のすべての RichTextBlock コンテナー スタイルに対する一般的なプロパティを提供します。

<!-- Usage -->
<RichTextBlock Style="{StaticResource BaseRichTextBlockStyle}">
    <Paragraph>Rich text.</Paragraph>
</RichTextBlock>

<!-- Style definition -->
<Style x:Key="BaseRichTextBlockStyle" TargetType="RichTextBlock">
    <Setter Property="FontFamily" Value="Segoe UI"/>
    <Setter Property="FontWeight" Value="SemiBold"/>
    <Setter Property="FontSize" Value="14"/>
    <Setter Property="TextTrimming" Value="None"/>
    <Setter Property="TextWrapping" Value="Wrap"/>
    <Setter Property="LineStackingStrategy" Value="MaxHeight"/>
    <Setter Property="TextLineBounds" Value="Full"/>
    <Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
</Style>

BodyRichTextBlockStyle

<!-- Usage -->
<RichTextBlock Style="{StaticResource BodyRichTextBlockStyle}">
    <Paragraph>Rich text.</Paragraph>
</RichTextBlock>

<!-- Style definition -->
<Style x:Key="BodyRichTextBlockStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaseRichTextBlockStyle}">
    <Setter Property="FontWeight" Value="Normal"/>
</Style>

: RichTextBlock スタイルには、TextBlock に含まれているテキスト見本スタイルのすべては含まれていません。これは主に、RichTextBlock のブロックベースのドキュメント オブジェクト モデルでは、個々のテキスト要素への属性の設定がより簡単になっているためです。 また、XAML コンテンツ プロパティを使って TextBlock.Text を設定する方式では、スタイルを設定できるテキスト要素が存在しない状況になるため、コンテナーにスタイルを設定する必要があります。 これに対して RichTextBlock では、テキスト コンテンツは常に Paragraph などの固有のテキスト要素になり、そこにページ ヘッダーやページ サブヘッダー、類似のテキスト見本定義の XAML スタイルを適用できるため、この問題はありません。

その他の名前付きスタイル

Button には、キーを持つ Style 定義の追加のセットを適用することもできます。これにより、既定の暗黙的なスタイルとは異なるスタイルを設定できます。

TargetType:ボタン

この Style は、ナビゲーション アプリの戻るボタンとして使うことができる Button 用の完全なテンプレートを提供します。 既定の寸法は 40 x 40 ピクセルです。 スタイルを調整するには、ButtonHeightWidthFontSize、その他のプロパティを明示的に設定するか、BasedOn を使って派生スタイルを作成します。

NavigationBackButtonNormalStyle リソースを適用した Button の例を以下に示します。

<Button Style="{StaticResource NavigationBackButtonNormalStyle}" />

これは、次のように表示されます。

A button styled as a back button

TargetType:ボタン

この Style は、ナビゲーション アプリの戻るボタンとして使うことができる Button 用の完全なテンプレートを提供します。 NavigationBackButtonNormalStyle と同様ですが、寸法は 30 x 30 ピクセルになります。

NavigationBackButtonSmallStyle リソースを適用した Button の例を以下に示します。

<Button Style="{StaticResource NavigationBackButtonSmallStyle}" />

テーマ リソースのトラブルシューティング

テーマ リソースの使用に関するガイドラインに従わないと、テーマに関連する予期しない動作がアプリで発生することがあります。

たとえば、淡色テーマのポップアップを開いたときに、濃色テーマのアプリの部分まで淡色テーマのように変更される場合があります。 または、淡色テーマのページに移動してから戻ってくると、元の濃色テーマのページ (またはその部分) が淡色テーマのように表示される場合もあります。

このような問題は、通常、"Default" テーマと "HighContrast" テーマを用意してハイ コントラスト シナリオをサポートした状態で、"Light" と "Dark" の両方のテーマをアプリ内の別々の部分で使っている場合に発生します。

たとえば、次のテーマ ディクショナリの定義について考えてみます。

<!-- DO NOT USE. THIS XAML DEMONSTRATES AN ERROR. -->
<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Default">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

直感的には、これは正しく見えます。 ハイ コントラストのときは myBrush が指す色を変更しますが、ハイ コントラストでない場合は、{ThemeResource} マークアップ拡張を利用することで myBrush がテーマに適した色を指すようにしています。 これは通常、アプリのビジュアル ツリー内に FrameworkElement.RequestedTheme が設定された要素がなければ期待どおりに動作します。 しかし、ビジュアル ツリーの各部分にテーマを再設定し始めたとたんに、アプリで問題が発生することになります。

問題が発生する原因は、他のほとんどの XAML 型とは異なり、ブラシが共有リソースであるためです。 XAML サブツリーに 2 つの要素があり、同じブラシ リソースを参照する別々のテーマが設定されている場合、フレームワークが各サブツリーを走査してそれぞれの {ThemeResource} マークアップ拡張表現を更新していくと、それにつれて共有ブラシ リソースに対する変更が他のサブツリーに反映されます。これは意図した結果とは異なります。

これを修正するには、"Default" ディクショナリを置き換えて、"HighContrast" に加えて "Light" テーマと "Dark" テーマのディクショナリも個別に指定します。

<!-- DO NOT USE. THIS XAML DEMONSTRATES AN ERROR. -->
<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Light">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="Dark">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

ただし、これらのリソースのいずれかが Foreground などの継承されたプロパティで参照されていると、引き続き問題が発生します。 カスタム コントロール テンプレートでは、{ThemeResource} マークアップ拡張を使って要素の前景色を指定している場合がありますが、継承された値がフレームワークによって子要素に伝達されるときは、{ThemeResource} マークアップ拡張表現で解決されたリソースへの直接参照が提供されます。 フレームワークがコントロールのビジュアル ツリーを走査する過程でテーマの変更が処理されると、問題が発生します。 フレームワークは {ThemeResource} マークアップ拡張表現を再評価して新しいブラシ リソースを取得しますが、この参照はまだコントロールの子に伝達されません。子への伝達は、次回の測定パスの間など、後で行われます。

結果として、テーマの変更に応答してコントロールのビジュアル ツリーを走査した後、フレームワークは子を走査し、それぞれに設定されている {ThemeResource} マークアップ拡張表現、または子のプロパティに設定されているオブジェクト上の表現をすべて更新します。 ここで問題が発生します。フレームワークがブラシ リソースを走査すると、その色は {ThemeResource} マークアップ拡張を使って指定されているため、ブラシ リソースが再評価されます。

この時点で、あるディクショナリのリソースに別のディクショナリから設定された色が適用され、フレームワークがテーマ ディクショナリを汚染した形になります。

この問題を解決するには、{ThemeResource} マークアップ拡張の代わりに {StaticResource} マークアップ拡張を使います。 ガイドラインを適用したテーマ ディクショナリは次のようになります。

<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Light">
      <SolidColorBrush x:Key="myBrush" Color="{StaticResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="Dark">
      <SolidColorBrush x:Key="myBrush" Color="{StaticResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

"HighContrast" ディクショナリでは、{StaticResource} マークアップ拡張ではなく {ThemeResource} マークアップ拡張が引き続き使われていることに注意してください。 この状況は、ガイドラインで既に説明した例外に当てはまります。 "HighContrast" テーマに使われるブラシの値のほとんどは、システムによって全体的に制御される色から選択されますが、これらは特別な名前付きのリソース (名前に "SystemColor" というプレフィックスが付いているもの) として XAML に公開されています。 コントラスト テーマ設定で使う特定の色は、コンピューターの簡単操作センターを通じてユーザーが設定できるようになっています。 これらの色の選択は、特別な名前付きのリソースに適用されます。 XAML フレームワークでは、システム レベルでの変更の検出時にこれらのブラシを更新する場合にも、同じテーマ変更イベントを使用します。 ここで {ThemeResource} マークアップ拡張が使われているのはこのためです。