アプリ自身で自分のテーマ(ダーク/ライト)を変更する

WinUI3関連記事
https://tera1707.com/entry/2022/02/06/144447#WinUI3

やりたいこと

以前、アプリ画面上にあるコントロール類を、テーマ(ダーク/ライト/ハイコントラスト)に沿った色でにするということをした。

tera1707.com

今回は、アプリ自身で、自分のテーマを変更できるということを下記の動画を見て知ったので、それを試してみる。

www.youtube.com

前提

  • Windows10 Home 21H1 19043.1706
  • VisualStudio2022 Community 17.1.4
  • WinUI3.0
  • Windows App SDK 1.1
  • 2022年5月の時点で調査実施

サンプルコード

<Window
    x:Class="ThemeChangeJikken.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
            <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="Red" BorderThickness="2">
                <TextBlock Text="ボタンのテーマの取得/変更"/>
                <Button Click="Button_Click_Get">今のテーマを取得</Button>
                <Button Click="Button_Click_Light">テーマを切り替え(ライト)</Button>
                <Button Click="Button_Click_Dark">テーマを切り替え(ダーク)</Button>
                <Button Click="Button_Click_Default">テーマを切り替え(Default)</Button>
                <Button Name="bt" Content="テーマ取得/変更ターゲット"/>
                <TextBlock Name="tbText"/>
            </StackPanel>

            <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="Blue" BorderThickness="2">
                <TextBlock Text="ウインドウのテーマ取得/変更"/>
                <Button Click="Button_Click_GetWIndowTheme">ウインドウのテーマを取得</Button>
                <Button Click="Button_Click_Light_Window">ウインドウのテーマを切り替え(ライト)</Button>
                <Button Click="Button_Click_Dark_Window">ウインドウのテーマを切り替え(ダーク)</Button>
                <Button Click="Button_Click_Default_Window">ウインドウのテーマを切り替え(Default)</Button>
                <TextBlock Name="tbText_Window"/>
            </StackPanel>
        </StackPanel>
    </Grid>    
</Window>
using Microsoft.UI.Xaml;
using System.Diagnostics;

namespace ThemeChangeJikken
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }

        // 今のテーマを取得(ボタン)
        private void Button_Click_Get(object sender, RoutedEventArgs e)
        {
            if (bt is FrameworkElement fe)
            {
                Debug.WriteLine(fe.ActualTheme.ToString());
                tbText.Text = fe.ActualTheme.ToString();
            }
        }

        // テーマを設定(ダーク)(ボタン)
        private void Button_Click_Dark(object sender, RoutedEventArgs e)
        {
            if (bt is FrameworkElement fe)
            {
                fe.RequestedTheme = ElementTheme.Dark;
            }
        }
        // テーマを設定(ライト)(ボタン)
        private void Button_Click_Light(object sender, RoutedEventArgs e)
        {
            if (bt is FrameworkElement fe)
            {
                fe.RequestedTheme = ElementTheme.Light;
            }
        }
        // テーマを設定(PCのテーマ設定に合わせる)(ボタン)
        private void Button_Click_Default(object sender, RoutedEventArgs e)
        {
            if (bt is FrameworkElement fe)
            {
                fe.RequestedTheme = ElementTheme.Default;
            }
        }

        ///////////////////////////////////////////////

        // 今のテーマを取得(ウインドウ)
        private void Button_Click_GetWIndowTheme(object sender, RoutedEventArgs e)
        {
            if (this.Content is FrameworkElement fe)
            {
                Debug.WriteLine(fe.ActualTheme.ToString());
                tbText_Window.Text = fe.ActualTheme.ToString();
            }
        }

        // テーマを設定(ライト)(ウインドウ)
        private void Button_Click_Light_Window(object sender, RoutedEventArgs e)
        {
            if (this.Content is FrameworkElement fe)
            {
                fe.RequestedTheme = ElementTheme.Light;
            }
        }

        // テーマを設定(ダーク)(ウインドウ)
        private void Button_Click_Dark_Window(object sender, RoutedEventArgs e)
        {
            if (this.Content is FrameworkElement fe)
            {
                fe.RequestedTheme = ElementTheme.Dark;
            }
        }

        // テーマを設定(Default)(ウインドウ)
        private void Button_Click_Default_Window(object sender, RoutedEventArgs e)
        {
            if (this.Content is FrameworkElement fe)
            {
                fe.RequestedTheme = ElementTheme.Default;
            }
        }
    }
}

動かしたのがこちら

コードについて

WindowのContentのActualThemeを見ると、アプリの今のテーマがわかる。
(WIndow.Contentは、今回の例だと、一番外側にあるGridになる。そいつのテーマを取得/設定する≒ウインドウのテーマを取得/設定する、になる)

各部品(Button等)のActualThemeを見ると、その部品の今のテーマがわかる。
(今回はButtonのテーマを見ている)

RequestedThemeに設定したいテーマを入れると、テーマを変更できる。

※注意:背景の色について

背景だけは、Fluent-xaml-theme-editorのreadmeにある通り、背景の色は、自分で設定しないといけない。

今回は、「ApplicationPageBackgroundThemeBrush」を一番外のstackpanelにセットした。(Lastly, don’t forget to set the background color of your page to the RegionColor・・・・のあたり参照)

※「ApplicationPageBackgroundThemeBrush」は、C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.22000.0\Generic\generic.xamlに定義されてる。

アプリのテーマ/コントロールのテーマ

今回、「Window.Content」「Button」のテーマを変えた。
「Window.Content」のテーマを変えればアプリのWIndow全体のテーマを変更でき、
「Button」のように、特定のコントロールのテーマを変えれば、そいつだけのテーマを変えることもできる。

画面内のボタンのテーマだけを変えてみたのがコレ。

あと、まだ未検証なのだが、「アプリ全体のテーマ」を変えるには、Application.Current.RequestedTheme)というのを使うっぽい。

→やってみたら、NotSupportedExceptionが出てうまくいかなかった。アプリ全体のテーマを変えたい場合は、Windowのthis.ContentRequestedThemeを変えればよさそう。(WindowsAppSDK1.1時点)

「ElementTheme Enum の Default」がなんなのか?

Windowsでは、RequestedThemeをDefaultに設定すると、常に「Dark」で、Windows Phoneでは、デフォルト値を使用すると、ユーザーが設定したシステムテーマになるとある。(ここでの「Windows」はデスクトップアプリで、「Windows Phone」は、パッケージしたアプリかな) →こちら

今回実験した、「前提」に書いた環境で試した限り、「Default」は、Windowsの「色」の設定の値をとってきている。

参考

アプリのテーマを変えれると知ったきっかけ動画

https://www.youtube.com/watch?v=w2XdbyNrXBQ

FrameworkElement.RequestedTheme Property

https://docs.microsoft.com/ja-jp/windows/winui/api/microsoft.ui.xaml.frameworkelement.requestedtheme?view=winui-3.0&viewFallbackFrom=winui-2.5

ElementTheme Enum

https://docs.microsoft.com/ja-jp/uwp/api/windows.ui.xaml.elementtheme?view=winrt-14393