generic.xamlにあるリソースの値を上書きしたときに、いうことを聞くヤツと聞かないヤツがいる(StaticResourceとThemeResourceの違い)

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

やりたいこと

WinUI3アプリで、デフォルトのコントロールの色の定義を上書きすることで、簡単に見た目をカスタムしたいということをした。

そのために、generic.xamlに書かれている色や数字を、自分のコードの中にも書くことで上書き(同じリソース(同じx:keyをもつリソース)を、別の値で定義する)とした。

ただ、今回、単に値を上書きしただけではいうことを聞いてくれないヤツがいた。

具体的には、RadioButtonの角の丸さを、自前のテンプレートを作らずにいじろうと思い、 デフォルトのテンプレートの中を見て、下図のコレを変えてみた。

で、下記のようなコードを書いてみたが、思った見た目にならない。

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

    <Grid>
        <Grid.Resources>
            <CornerRadius x:Key="ControlCornerRadius">20</CornerRadius>
        </Grid.Resources>

        <StackPanel>
            <RadioButton Content="AAAAAA"  Background="Orange"/>←★コレ★
        </StackPanel>
    </Grid>
</Window>

見た目。 もっと角が丸くなってほしいのに、まるくならない。

なぜなのか?調べる。

結論

generic.xamlの該当のリソースが使われるときに、

Value="{StaticResource ControlCornerRadius}"

という感じで、StaticResourceとして使われているため。

これが、もし

Value="{ThemeResource ControlCornerRadius}"

のように、ThemeResourceであれば、そのリソースを上書きしてやれば、その値が反映される。

どうしてそのような違いが生まれるか、は、はっきりした理由は分からないのだが、StaticResourceとThemereResourceの、リソースを見に行くタイミングの違い、っぽい。

実験

下記2つのコントロール、同じControlCornerRadiusという値を使っている。

generic.xamlより

ただし、それぞれのstyle(テンプレート)の中で、

  • ComboBox
    • その値を、ThemeResourceとして使っている
  • RadioButton
    • その値を、StaticResourceとして使っている

という違いがある。

ComboBox

    <Style x:Key="DefaultComboBoxStyle" TargetType="ComboBox">
        <Setter Property="Padding" Value="{ThemeResource ComboBoxPadding}" />
        <Setter Property="MaxDropDownHeight" Value="504" />
        <Setter Property="Foreground" Value="{ThemeResource ComboBoxForeground}" />
        <Setter Property="Background" Value="{ThemeResource ComboBoxBackground}" />
        <Setter Property="BorderBrush" Value="{ThemeResource ComboBoxBorderBrush}" />
        <Setter Property="BorderThickness" Value="{ThemeResource ComboBoxBorderThemeThickness}" />
        <Setter Property="TabNavigation" Value="Once" />
        <Setter Property="TextBoxStyle" Value="{StaticResource ComboBoxTextBoxStyle}" />
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto" />
        <Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True" />
        <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
        <Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Top" />
        <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
        <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
        <Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}" />
        <Setter Property="primitives:ComboBoxHelper.KeepInteriorCornersSquare" Value="true" />
        <Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />←★コレ★
  ・
  ・
  ・

RadioButton

    <Style x:Key="DefaultRadioButtonStyle" TargetType="RadioButton">
        <Setter Property="Background" Value="{ThemeResource RadioButtonBackground}" />
        <Setter Property="Foreground" Value="{ThemeResource RadioButtonForeground}" />
        <Setter Property="BorderBrush" Value="{ThemeResource RadioButtonBorderBrush}" />
        <Setter Property="Padding" Value="8,6,0,0" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalContentAlignment" Value="Left" />
        <Setter Property="VerticalContentAlignment" Value="Top" />
        <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
        <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
        <Setter Property="MinWidth" Value="120" />
        <Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
        <Setter Property="FocusVisualMargin" Value="-7,-3,-7,-3" />
        <Setter Property="CornerRadius" Value="{StaticResource ControlCornerRadius}" />←★コレ★
  ・
  ・
  ・

で、自分の画面のxamlの中に、ControlCornerRadiusのリソースを入れて奴やる。

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

    <Grid>
        <Grid.Resources>
            <CornerRadius x:Key="ControlCornerRadius">20</CornerRadius>
        </Grid.Resources>

        <StackPanel>
            <RadioButton Content="AAAAAA"  Background="Orange"/>
            <ComboBox SelectedIndex="0" Background="Red">
                <x:String>AAAA</x:String>
                <x:String>BBBB</x:String>
            </ComboBox>
        </StackPanel>
    </Grid>
</Window>

これを動かすと、

という感じで、StaticResourceとして値を使っているRadioButtonの角は丸くならず、
ThemeResourceとして値を使っているComboBoxの方だけ丸くなる。

generic.xamlにあるリソースの値で、値を上書できるのは、ThemeResourceとして使われているもの、と思った方がよさそう。

思ったこと

ThemeResourceだったら、自前で同じリソースを定義したら上書きできるのだと分かったが、 上で試したControlCornerRadiusのような、複数のコントロールで使われている値を安易に上書することはやめた方がよさそう。

思ってもないコントロールの角が丸くなった、とかに、きっとなる。

デフォルトのコントロールで使われるリソース値を上書きする場合は、MSの公式が言うように、特定のコントロールの名前がKey名についているような、「そのコントロール向けのリソース値」のみにした方がよいと思う。

例えばこういうリソース。

総合して、どういうときにリソースの上書きを行って、どういうときにgeneric.xamlのstyleをマルっとコピーすればいいのか?

個人的な考えだが、

generic.xamlにあるデフォルトのstyle、テンプレートの中で、Themeresourceとして使われているリソースの値を上書きするだけで、望みの見た目にできるコントロールは、リソースの上書きだけすればよい。

そうでないもの、つまり上記だけではすまない、デフォルトの見た目をそもそも変えたい(線を追加したい、〇〇を配置したい、変えたい値がstaticresourceや直値で書かれてる、など)ような場合は、generic.xamlにあるデフォルトのstyleを自前のコードにコピーしてきて、それをカスタムすればよい。

と思う。

参考

MS公式
このあたりに大事なことが書かれてるっぽいが、頭が追い付かない。。。

https://learn.microsoft.com/ja-jp/windows/apps/design/style/xaml-resource-dictionary#lookup-behavior-for-xaml-resource-references

リソース キーをオーバーライドして Expander コントロールをカスタマイズできません
→ズバリ知りたいことではないが、StaticresourceだとUIがリソースのxamlを検索する機会がないが、Themeresourceだとある、みたいなことが書いてる。 あと、ControlTemplateのコピーを作って、そこをカスタムすることで解決できたとも書いてる。やっぱりその解決方法でよいのだと思う。

https://github.com/microsoft/microsoft-ui-xaml/issues/6688