Reusable ResourceDictionaries with Overrides

Kevin Delval 186 Reputation points
2021-05-23T15:56:06.307+00:00

Hello,

I was hoping someone might be able to help me with this.

I am attempting to create a flexible Styles library where I can define a number of default styles for various controls, using default variables for padding, margin, color, etc. However, I want to be able to override these variables or even the styles themselves in apps that use the resource dictionaries from that external library. Basically, I get a kind of generic look-and-feel across all my apps, but if I define another Color with x:Key="PrimaryColor" I want the styles from those external resource dictionaries to use that color instead (naturally, the keys have to be the same).

I have already created some test .xaml files with a code-behind that inherit from ResourceDictionary and define some test values for Thickness. I am able to reference these in my app and apply them to views. However, the following results in a XamlParseException (StaticResource not found for key ThicknessValueNormal):

<!-- Thickness.xaml -->

<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary x:Class="Sigma.XF.Theming.Variables.Thickness"
                    xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
 <!--  All  -->
 <Thickness x:Key="ThicknessNormal"
            Bottom="{StaticResource ThicknessValueNormal}"
            Left="{StaticResource ThicknessValueNormal}"
            Right="{StaticResource ThicknessValueNormal}"
            Top="{StaticResource ThicknessValueNormal}" />
</ResourceDictionary>

<!-- Spacings.xaml -->

<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary x:Class="Sigma.XF.Theming.Variables.Spacings"
                    xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
 <!--  Padding/Margin  -->
 <x:Double x:Key="ThicknessValueMini">5.0</x:Double>
 <x:Double x:Key="ThicknessValueSmall">10.0</x:Double>
 <x:Double x:Key="ThicknessValueNormal">15.0</x:Double>
 <x:Double x:Key="ThicknessValueLarge">25.0</x:Double>
 <x:Double x:Key="ThicknessValueXLarge">35.0</x:Double>
 <x:Double x:Key="ThicknessValueHuge">50.0</x:Double>


 <!--  Spacing  -->
 <x:Double x:Key="SpacingMini">5.0</x:Double>
 <x:Double x:Key="SpacingSmall">10.0</x:Double>
 <x:Double x:Key="SpacingNormal">15.0</x:Double>
 <x:Double x:Key="SpacingLarge">20.0</x:Double>
 <x:Double x:Key="SpacingXLarge">25.0</x:Double>
 <x:Double x:Key="SpacingHuge">30.0</x:Double>

 <x:Double x:Key="SpacingButtonMini">48.0</x:Double>
 <x:Double x:Key="SpacingButtonSmall">36.0</x:Double>
 <x:Double x:Key="SpacingButtonNormal">24.0</x:Double>
 <x:Double x:Key="SpacingButtonLarge">12.0</x:Double>
 <x:Double x:Key="SpacingButtonXLarge">8.0</x:Double>
 <x:Double x:Key="SpacingButtonHuge">4.0</x:Double>
</ResourceDictionary>


<FlyoutPage.Detail>
 <ContentPage Title="Test">
 <StackLayout BackgroundColor="Red">
 <ContentView BackgroundColor="Blue"
  VerticalOptions="CenterAndExpand">
 <Label BackgroundColor="Magenta"
    HorizontalOptions="Center"
    Padding="{StaticResource ThicknessNormal}"
        Text="Test" />
 </ContentView>
 </StackLayout>
 </ContentPage>
 </FlyoutPage.Detail>

When I attempt to use ThicknessNormal, it is unable to find ThicknessValueNormal. I can solve this by adding a reference to the Spacings.xaml in Thickness.xaml, but then I am no longer able to override those doubles in an app actually making use of these styles, as the resource lookup mechanism will always prioritize the locally declared keys over any external ones.

As far as I can tell, it seems that the ResourceDictionary's lookup does not reach my App.Xaml, where I proceed to merge both the Spacings.xaml and Thickness.xaml files:

<?xml version="1.0" encoding="utf-8" ?>
<Application x:Class="Sigma.XF.Sample.App"
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:variables="clr-namespace:Sigma.XF.Theming.Variables;assembly=Sigma.XF">
 <Application.Resources>
 <ResourceDictionary>
 <ResourceDictionary.MergedDictionaries>
 <!--  Variables  -->
 <variables:Spacing />
 <variables:Thickness />

 <!--  This is where I would like to define overrides for colors, spacings, margins, etc.  -->
 </ResourceDictionary.MergedDictionaries>
 </ResourceDictionary>
 </Application.Resources>
</Application>

Now the question is: is there something I am overlooking or is what I am attempting to do simply not possible? I know one fix would be to simply copy all the resource dictionary xamls and just change whatever values I need different for a specific app, but that doesn't really solve the issue of wanting to prevent code duplication, since any updates I make to the style library will need to be copied over again (and changed values reset).

Thanks in advance.

EDIT:

What I mean is that I would like to be able to declare an <x:Double> with the same Key but a different value in an app making use of those styles, thereby changing the defaults. So I can solve the runtime XamlParseException by doing the following:

<?xml version="1.0" encoding="UTF-8" ?>
 <ResourceDictionary x:Class="Sigma.XF.Theming.Variables.Thickness"
                     xmlns="http://xamarin.com/schemas/2014/forms"
                     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
      <ResourceDictionary.MergedDictionaries>
        <variables:SpacingsDictionary />
    </ResourceDictionary.MergedDictionaries>

     <!--  All  -->
     <Thickness x:Key="ThicknessNormal"
                Bottom="{StaticResource ThicknessValueNormal}"
                Left="{StaticResource ThicknessValueNormal}"
                Right="{StaticResource ThicknessValueNormal}"
                Top="{StaticResource ThicknessValueNormal}" />
 </ResourceDictionary>

But if I do this, then ThicknessNormal will only ever use the value with key ThicknessValueNormal from the Spacings.xaml file. So, in an app using this style library, I am no longer able to redefine a Thickness with the same key, but a different value.
A better use case would be with colors, where I might want different primary and secondary colors, but I want all my styles imported from that external library to function as normal, using those new colors.

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,262 questions
{count} votes

Accepted answer
  1. Kevin Delval 186 Reputation points
    2021-09-25T18:43:54.44+00:00

    I've managed to find a solution to the problem.

    The use of DynamcResources actually solved the issue quite nicely. I wasn't aware of how they worked. But they allow me to define optional defaults on a variety of variables (colors, thicknesses, etc) which can then be included, overriden or left out entirely whenever I want to reuse these styles.

    0 comments No comments

0 additional answers

Sort by: Most helpful