William Jones
William Jones

Reputation: 18279

UWP/XAML: What is the difference between theming in a page's Resources, in a separate ResourceDictionary file, or in App.xaml?

I have a simple UWP app that only has a single page. In trying to override the SystemAccentColor (see other questions such as this one), I discovered that I would get different results in each of the 3 valid locations to add the code:

  1. In the Resources section of the page itself. (Does nothing)

  2. In a separate ResourceDictionary file. (Only partially works)

  3. In the App.xaml file. (Works)

This is the Page.Resources for my single page, with my custom styles contained in Dictionary.xaml :

<Page.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Page.Resources>

Here is the SystemAccentColor code I am inserting in each of the 3 locations, henceforth referred to below as //THE CODE//:

<Color x:Key="SystemAccentColor">Red</Color>

<SolidColorBrush x:Key="SystemControlBackgroundAccentBrush" Color="{ThemeResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlDisabledAccentBrush" Color="{ThemeResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlForegroundAccentBrush" Color="{ThemeResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAccentBrush" Color="{ThemeResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAltAccentBrush" Color="{ThemeResource SystemAccentColor}" />
<SolidColorBrush x:Key="SystemControlHighlightAltListAccentHighBrush" Color="{ThemeResource SystemAccentColor}" Opacity="0.9" />
<SolidColorBrush x:Key="SystemControlHighlightAltListAccentLowBrush" Color="{ThemeResource SystemAccentColor}" Opacity="0.6" />
<SolidColorBrush x:Key="SystemControlHighlightAltListAccentMediumBrush" Color="{ThemeResource SystemAccentColor}" Opacity="0.8" />
<SolidColorBrush x:Key="SystemControlHighlightListAccentHighBrush" Color="{ThemeResource SystemAccentColor}" Opacity="0.9" />
<SolidColorBrush x:Key="SystemControlHighlightListAccentLowBrush" Color="{ThemeResource SystemAccentColor}" Opacity="0.6" />
<SolidColorBrush x:Key="SystemControlHighlightListAccentMediumBrush" Color="{ThemeResource SystemAccentColor}" Opacity="0.8" />
<SolidColorBrush x:Key="SystemControlHyperlinkTextBrush" Color="{ThemeResource SystemAccentColor}" />
<SolidColorBrush x:Key="ContentDialogBorderThemeBrush" Color="{ThemeResource SystemAccentColor}" />
<SolidColorBrush x:Key="JumpListDefaultEnabledBackground" Color="{ThemeResource SystemAccentColor}" />

Resources section of Page

Inserting //THE CODE// into Page.Resources does nothing.

<ResourceDictionary>

    <ResourceDictionary.ThemeDictionaries>
        //THE CODE//
    </ResourceDictionary.ThemeDictionaries>

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Dictionary.xaml"/>
    </ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

Specifying that this should apply to the Light theme also does nothing.

<ResourceDictionary>

    <ResourceDictionary.ThemeDictionaries>
        <ResourceDictionary x:Key="Light">
            //THE CODE//
        </ResourceDictionary>
    </ResourceDictionary.ThemeDictionaries>

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Dictionary.xaml"/>
    </ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

ResourceDictionary

Inserting //THE CODE// into Dictionary.xaml also does nothing.

<ResourceDictionary.ThemeDictionaries>
    //THE CODE//
</ResourceDictionary.ThemeDictionaries>

However, specifying the Light/Dark theme causes the color override to apply, but ONLY to my custom styles, which are also defined in Dictionary.xaml. However, it does properly apply only in the case the Windows light/dark theme matches.

<ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Light">
        //THE CODE//
    </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

App.xaml

Inserting //THE CODE// in App.xaml just works.

<Application.Resources>
    <ResourceDictionary>
        //THE CODE//
    </ResourceDictionary>
</Application.Resources>

Edited: I also can specify Dark/Light theme as an x:Key, and everything works as expected.

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Light">
                //THE CODE//
        </ResourceDictionary.ThemeDiciontaries>
    </ResourceDictionary>
</Application.Resources>

Wrap up

So in summary, what the heck is going on here? This clearly looks like some sort of scope issue, but I can't find anything in the developer documentation that explains the behavior I'm seeing here.

Upvotes: 4

Views: 810

Answers (1)

marcelwgn
marcelwgn

Reputation: 979

The reason why a lot of those don't work is that ResourceDictionary.ThemeDictionaries expects it's content to be resource dictionaries with keys corresponding for the individual items. If you put regular resources, such as theme brushes in there, the theme resource lookup doesn't know what to do, hence it is not picking up the resources.

If you specify resources inside a themedictionary, and also load a dictionary that contains that resource, the resource dictionary is overwriting the theme resource dictionary value, so that is why mixing the theme dictionaries and loading a resource dictionary in the merged dictionaries is generally a bad idea.

In the case you only specify a resource in the theme dictionary "light", when the apps/pages dictionary is in light theme, it gets picked up by the resource look up. If the page is in dark theme however, it will look into the "dark" dictionary, which in your case is missing, and then goes up the tree until it finds a dark theme dictionary, which has the resource specified. So to override that resource in all themes, you need to specify the resource in all theme dictionaries (light, dark and default).

Upvotes: 1

Related Questions