locked
A bug in MergedDictionaries and ThemeDictionaries.

    General discussion

  • Hi everyone. I found a bug when developing in .NET for WinRT with XAML. To reproduce the bug, follow these steps:

    (1) Create an empty Windows Store app with XAML, call it "ReproduceDictIssue".

    (2) Place the following AppBarButton on the main page so that you can see the effect of other steps:

    <AppBarButton Icon="Accept"/>

    (3) Run the app to see a dark themed app page with a white AppBarButton. Close the app.

    (4) Replace App.xaml with the following code: (If you didn't name the project as ReproduceDictIssue, you might want to change the corresponding part so that the code works correctly for you)

    <Application
        x:Class="ReproduceDictIssue.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:ReproduceDictIssue"
        RequestedTheme="Dark">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.ThemeDictionaries>
                    <ResourceDictionary x:Key="Dark">
                        <SolidColorBrush x:Key="AppBarItemForegroundThemeBrush" Color="Red"/>
                    </ResourceDictionary>
                </ResourceDictionary.ThemeDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    (5) Run the app, now the app bar button is red. NOTE that the "Accept" (tick) icon is also red. Close the app.

    (6) Replace the App.xaml with the following code:

    <Application
        x:Class="ReproduceDictIssue.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:ReproduceDictIssue"
        RequestedTheme="Dark">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.ThemeDictionaries>
                    <ResourceDictionary x:Key="Dark">
                        <SolidColorBrush x:Key="AppBarItemForegroundThemeBrush" Color="Red"/>
                    </ResourceDictionary>
                </ResourceDictionary.ThemeDictionaries>
                
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    That is, merge a dictionary to it.

    (7) Run the app. No difference here. Close the app.

    (8) Replace App.xaml with the following code:

    <Application
        x:Class="ReproduceDictIssue.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:ReproduceDictIssue"
        RequestedTheme="Dark">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.ThemeDictionaries>
                    <ResourceDictionary x:Key="Dark">
                        <SolidColorBrush x:Key="AppBarItemForegroundThemeBrush" Color="Red"/>
                    </ResourceDictionary>
                </ResourceDictionary.ThemeDictionaries>
                
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary/>
                </ResourceDictionary.MergedDictionaries>
    
                <Style TargetType="AppBarButton">
                    <Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}"/>
                </Style>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    That is, add a default AppBarButton style to it.

    (9) Run the app. NOTE that the "Accept" (tick) icon is now WHITE, which is indeed the unoverriden value. Close the app.

    (10) Remove this line:

    <Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}"/>

    (11) Run the app. Now the color is correct again. Close the app.

    (12) Add the line back:

    <Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}"/>

    And remove this line (this line is in the ResourceDictionary.MergedDictionaries):

    <ResourceDictionary/>

    (13) Run the app. The color is still correct. Close the app.

    (14) Replace App.xaml with the following code:

    <Application
        x:Class="ReproduceDictIssue.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:ReproduceDictIssue"
        RequestedTheme="Dark">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary>
                        <ResourceDictionary.ThemeDictionaries>
                            <ResourceDictionary x:Key="Dark">
                                <SolidColorBrush x:Key="AppBarItemForegroundThemeBrush" Color="Red"/>
                            </ResourceDictionary>
                        </ResourceDictionary.ThemeDictionaries>
                    </ResourceDictionary>
                    <!-- <ResourceDictionary/> -->
                </ResourceDictionary.MergedDictionaries>
    
                <Style TargetType="AppBarButton">
                    <Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}"/>
                </Style>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    That is, move the theme dictionary into the merged dictionary.

    (15) Run the app. The color is still correct. Close the app.

    (16) Now uncomment this line:

    <!-- <ResourceDictionary/> -->

    (17) Run the app. The color of "Accept" (tick) icon is white again.

    Conclusion: there is a problem in theme resource look-up. For a Style's Setter, the look-up might be in the following order:

    Last meged dictionary -> second last -> ... -> first merged dictionary -> specific theme dictionary(like Light/Dark/HighContrastWhite) -> general theme dictionary(like Default) -> programmer-specified entries -> system entries.

    Note that the bold part is recursive. Due to this behaviour, the following dictionary:

    <ResourceDictionary> <ResourceDictionary.ThemeDictionaries> <ResourceDictionary x:Key="Dark"> <!-- 2 --> <SolidColorBrush x:Key="AppBarItemForegroundThemeBrush" Color="Red"/> </ResourceDictionary> </ResourceDictionary.ThemeDictionaries> <ResourceDictionary.MergedDictionaries> <ResourceDictionary/> <!-- 1 --> </ResourceDictionary.MergedDictionaries>
    <!-- 3 --> <Style TargetType="AppBarButton"> <Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}"/> </Style> </ResourceDictionary>


    resolves the ThemeResource AppBarItemForegroundThemeBrush in the following steps:

    It first asks dictionary 1 to resolved ThemeResource AppBarItemForegroundThemeBrush.

    Dictionray 1 has no merged dictionaries, and has no theme dictionaries. It looks at programmer-specified entries, which is empty. So it asks for system entries. Note that dictionary 1 does not have AppBarItemForegroundThemeBrush overriden, so it returns the system default value for dark theme, which is white.

    The large dictionary now finishes the look-up with the value returned from dictionary 1, which is white.

    In that process dictionary 2 is never asked to look up AppBarItemForegroundThemeBrush.

    We might find that this process' behaviour is out of most people's expectation. ResourceDictionary should suppress system default resources when doing a look-up that is in a larger dictionary's recursive part. Actually IT DOES. The proof is that if the resource is not in a Setter, it has the expected behaviour. Like in a control template or anything. You might have noticed that the circle of the app bar button is pink and if the setter is removed, the color of "Accept" (tick) icon is correct. That'll be the proof.

    Thanks for reading this bug report. I hope someone from Microsoft can follow this bug and remove the bug in future releases of .NET for WinRT with XAML. Thanks again, everyone.

    Monday, February 23, 2015 1:04 PM

All replies