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

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

参考書

WinUI3

WinUI3でアプリを作ろうと思ったときのとっかかりによかった。 msdocsに書いてある情報を、体系的に、順番に読みたいな、というときによいかも。(ただし英語)
この本で分からなかった、かゆいところに手が届かなかった部分を私は記事にしてる感じ。

C#①

表紙に書いてある通り、教科書として最適。 これからC#を勉強したいけど、ネットだけで勉強するのは効率が悪いから体系的に学べる本が欲しいときや、 ちょっとC#を勉強してコード書けるようになったけど、もう少し広く深く知りたいなというときによいと思う。
私は仕事で触れるコードを軸に、基本ネットで断片的にC#を学んだので、その知識の隙間を埋めて枝葉を広げるためにとても分かりやすかった。

C#②

C#の文法的に色々できるのは分かったが、いざ実装するときに、わかったことを使ってどう実装すればいいのか?と悩んだときに指針になりそうな本。
「プロパティ等の名前の付け方、どうすればいい?」「情報をクラス外部に見せるときに、プロパティにすべき?メソッドにすべき?」「異常だと判定したいとき、どんなときにどんな例外をスローすべき?」などなど、勉強になる部分が山ほどあった。
私のように「コードは書くけどこれであってるのか自信がない、レビューで指摘されるのが嫌だ、実装時の(心の)よりどころが欲しい」という人に最適。