Fallback for missing style in themable application without applied theme

248 views Asked by At

I have an application that uses several custom themes that can be switched at runtime. The themes, the application and the UI (UserControls, dialogs etc.) each are in a separate .NET assembly.

The themes not only override the implicit default styles of controls (new control template), but also provide various explicit styles.

Example:

<!-- "Themes" assembly -->
<Style TargetType="Button" x:Key="DialogButtonStyle" 
       BasedOn="{StaticResource BaseButtonStyle}">
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="Height" Value="24" />
    <Setter Property="MinWidth" Value="86" />
    <Setter Property="Padding" Value="8 0" />
</Style>

<!-- "UI" assembly -->
<Button Style="{DynamicResource DialogButtonStyle}"
        Content="OK" />

Themes are applied by merging all their styles, brushes etc. into the application resources, e.g.:

<Application x:Class="GUIDemo.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>    
        <ResourceDictionary x:Name="MainDictionary">
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary x:Name="ThemeDictionary">
                    <ResourceDictionary.MergedDictionaries>
                        <ResourceDictionary Source="/Themes;component/SystemTheme.xaml" />
                    </ResourceDictionary.MergedDictionaries>
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>    
    </Application.Resources>
</Application>

(Or corresponding code at runtime.)

Question:

Is it possible to define fallback styles in my UI assembly (not the application assembly)? These should be automatically applied if the application has not included any of the themes, i.e. when keys like "DialogButtonStyle" are undefined and cause error messages in the output.

What I want to achieve is that my UI library is fully themable, but can also be used without having to care about the custom themes.

What I tried:

(1) Define a dummy App.xaml that contains the fallback style. This works at design time (designer preview, code completion) but not at runtime.

(2) Define the styles in "Themes/Generic.xaml". This seems to work only for implicit styles, not explicit styles.

(3) Merge the Generic.xaml into the resources of all XAML files that use the style. This does not work because the style now overrides the theme.

2

There are 2 answers

3
mm8 On

Try to merge the fallback styles into your ResourceDictionary before you merge the theme resources:

<Application.Resources>
    <ResourceDictionary x:Name="MainDictionary">
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="FallbackResources.xaml" />
            <ResourceDictionary Source="/Themes;component/SystemTheme.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Then the theme resources - if any - should override the fallback resources.

No, that's not what I meant. I want the fallbacks to be defined in the UI assembly (not the application). The whole point of the fallbacks is to make sure the styles are defined even if an application does properly apply the theme (or other resource files).

There is no way for the application to be able to apply these fallback resources unless you merge them into your application. If the application fails to apply any resource dictionary from the external assembly you will get runtime exceptions if you try to reference any of these resources in your views. The resources must be explicitly merged into the application somehow.

0
Scott Solmer On

I too am building a control library that supports themes and ran into this issue. You want someone to be able to use the control in a project that doesn't define any themes (and not look weird), but at the same time support theme colors.

When no theme is added to an App's resources, the theme colors (resource keys) aren't found and the controls revert to their defaults. For example, black text on white background for a TextBox. In the case of a Grid, it is transparent and that is a problem for a pop-up control based on grid.

The solution is an Implicit Style.

Basically you define a style at the level you need it (for example in the grid, or at the control level) which targets a specific type and does not use an x:key.

Related question:
Implicit styles in Application.Resources vs Window.Resources?