ResourceDictionaryの中のリソースを、Key名を指定して取得する(ハイコントラストモード対応、システムカラー(SystemColors.XXXXX)の取得)

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

Windowsには、ハイコントラストモードというのがあるが、 WPFアプリで、その見た目の変化にアプリのUIを対応させたくなった。

で、対応する際に、

  • リソースディクショナリに定義したブラシを、
  • 同じKeyで、
  • C#のコードから取得する

ということをしたくなった。

そのやり方を調べる。

やったこと

下記のようにする。

  • ハイコントラストとそうでないときのResourceDictionaryを作成する
  • それぞれに、同じKeyの<SolidColorBrush>をつくる
  • そのBrushを、FrameworkElement.FindResource でkey名をもとに取得する
  • それを、xamlに書いたコントロールに適用する

各モードのResourceDictionaryを作成し、同じKeyのブラシをつくる

WPFでのやり方は、下記の過去記事を参照。

https://tera1707.com/entry/2024/11/16/223546

↑の記事では、KeyのないStyleを定義して、そのTargetTypeのコントロール全部に反映されるようなことをしているが、今回はStyleではなく、ただのブラシをつくる。

ブラシをそれぞれのDictionaryで、同じKeyで定義する。

Dictionary1.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <SolidColorBrush x:Key="MyColor" Color="#FFFF0000"/>
</ResourceDictionary>

Dictionary2.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <SolidColorBrush x:Key="MyColor" Color="{DynamicResource {x:Static SystemColors.HotTrackColorKey}}"/>
</ResourceDictionary>

C#から、FindResourceでそのブラシを取得する

FindResourceを使って、下記のようにする。

今回は、ボタンをおしたときにブラシを取得し、Ellipse(四角形)のStrokeにそのブラシをセットする。

Windowのコードビハインド内

切替の実施はここでやる

private void Button_Click(object sender, RoutedEventArgs e)
{
    // リソースディクショナリをまず適用して、
    ((App)Application.Current).ChangeTheme(themeSwitch);
    themeSwitch = !themeSwitch;

    // その中のリソースを、キー名をもとに取得する
    ellipse.Stroke = (SolidColorBrush)this.FindResource("MyColor");
}

App.xaml.cs

ここがディクショナリ切り替え処理を持ってる

private void Application_Startup(object sender, StartupEventArgs e)
{
    dict = new ResourceDictionary();
    Application.Current.Resources.MergedDictionaries.Add(dict);
}

public void ChangeTheme(bool themeSwitch)
{
    dict!.Source = new Uri($"pack://application:,,,/ResourceDictionary/Dictionary{(themeSwitch ? 1 : 2)}.xaml");
}

前の記事でやった、リソースの切り替えをまずやっている。

使うディクショナリをまずセットしておいて、その中のリソースをとる、という感じ。
(リソースディクショナリを使っていないと、FindResourceでエラーになる。)

これで、リソースディクショナリの中のBrushを取れる。

こううごく

ボタンを押すたびにディクショナリが切り替わって、Ellipseの色が変わる。

SolidColorBrushのColorにSystemColors.XXXXXを使用する

上のDictionary2.xamlで、SystemColors.HotTrackColorKeyという色を使っている。

こんな感じで書いている。

<SolidColorBrush x:Key="MyColor" Color="{DynamicResource {x:Static SystemColors.HotTrackColorKey}}"/>

この「SystemColors.HotTrackColorKey」というのは、Windowsのハイコントラスト設定画面でいうところの

この色を指している。

ハイコントラストモード時は、ユーザーがこの画面で設定した色を、SystemColors.XXXXXを使って取ってきて、コントロールの色に使う。

今回、このSystemColors.HotTrackColorKey を、

<SolidColorBrush x:Key="MyColor" Color="{DynamicResource {x:Static SystemColors.HotTrackBrushKey}}"/>

と、間違えて「HotTrackBrushKey」と書いて、下記のような例外を吐かせてしまっていた。

これはおそらく、「HotTrackBrushKey」はブラシそのもののリソースのキーだから、Colorに適用してよいキーじゃないよ!ということだと思う。多分。

その他

ハイコントラストの対応するにあたり、SystemColorsのどの色を使うか?は、要検討。

過去の↓の記事で調べたwinuiのハイコントラスト対応で、winuiのどのSystemColorがどのハイコントラストの色設定に該当するか?を調べた。

https://tera1707.com/entry/2022/04/14/232415

たぶん、そのwinuiの色名に似た色が使われてるんだろうな、とは思う。

参考

FrameworkElement.FindResource(Object) メソッド

https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.frameworkelement.findresource?view=windowsdesktop-9.0

WPFでアプリ起動中にResourceDictionaryを切り替えて、見た目を変える過去記事

https://tera1707.com/entry/2024/11/16/223546

WPFのSystemColorsの一覧

https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.systemcolors?view=windowsdesktop-9.0