12

I am building a Xamarin Forms Application and I am currently drawing up my application Resources, mainly my colours.

For example I have the following:

  <Color x:Key="Slate">#404040</Color>
  <Color x:Key="Blue">#458623</Color>

  <Color x:Key="SelectedItemColour">#458623</Color>

As you can see my SelectedItemColour is the same as the Blue.

I have tried the following but it didn't work:

  <Color x:Key="Slate">#404040</Color>
  <Color x:Key="Blue">#458623</Color>

  <Color x:Key="SelectedItemColour" Color="{StaticResource Blue}"/>

I know if WPF you can do the answer stated here

Is it possible to point a Colour Resource to another Colour Resource in Xamarin.Forms?

Community
  • 1
  • 1
JKennedy
  • 18,150
  • 17
  • 114
  • 198

4 Answers4

5

This may be an old question, but I was trying to accomplish this same thing for a project today and wanted a more elegant solution than the one proposed here. There doesn't seem to be a way to accomplish it purely with XAML, but this is the solution I ended up using.

First, I defined a utility class named ColorReference:

public class ColorReference : INotifyPropertyChanged
{
    private Color color = Color.Black;

    public Color Color
    {
        get => this.color;
        set
        {
            this.color = value;
            this.OnPropertyChanged();
        }
    }

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    [ NotifyPropertyChangedInvocator ]
    protected virtual void OnPropertyChanged([ CallerMemberName ] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion

    public static implicit operator Color(ColorReference colorReference) => colorReference.Color;
}

I'm not 100% positive that the INotifyPropertyChanged implementation is necessary but I figured it couldn't hurt (potentially allowing changing the color at runtime; I haven't tested this).

To use it, simply use it as a resource in a ResourceDictionary:

<Color x:Key="FirstColor">#51688f</Color>
...
<utility:ColorReference x:Key="SomeOtherColorName" Color="{StaticResource FirstColor}" />

In my use case, I'm using it to style Telerik controls with colors defined in my theme so that if I make a new theme, I don't need to copy the same color value all over the place. The obvious downside is that for any type other than Color, a new class would need to be defined, but I doubt I will need too many types to be aliased like this. Hopefully this helps someone else in the future trying to do the same thing I'm doing.

Arcanox
  • 1,420
  • 11
  • 20
3

You can use x:Static in tandem with a static class in order to directly reference those colors by name. This has the benefits of keeping the colors centralized into one class and minimizing the amount of XAML.

namespace ResourceColors
{
    public static class Colors
    {
        public static Color Slate = Color.FromHex("#404040");
    }
}


<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:ResourceColors;assembly=ResourceColors" x:Class="ResourceColors.PageOne">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Color x:Key="Blue">#458623</Color>
        </ResourceDictionary>
    </ContentPage.Resources>
    <ContentPage.Content>
        <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
            <Label Text="Test" TextColor="{x:Static local:Colors.Slate}" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>
Paul
  • 1,175
  • 8
  • 15
  • Does this mean you create a new file Colors.cs? Where do you put it then, in Model, ViewModels, Views or elsewhere? TY – s3c Feb 12 '20 at 12:17
0

So the way I had to do this was very complex so listen carefully.

I found that the real problem here was that I needed to "Theme" my Xamarin.Forms application.

So heres what I did.

First made an abstract Theme class:

public abstract class Theme
{
    public Dictionary<string, Color> Colours { get; set; } = new Dictionary<string, Color>();

    protected void DictionaryThemes(Type type)
    {
        var properties = type.GetRuntimeProperties();
        foreach (PropertyInfo propInfo in properties)
        {
            if (propInfo.PropertyType == typeof(Color))
            {
                var key = propInfo.Name;
                var value = propInfo.GetValue(this);
                Colours.Add(key, (Color)value); 
            }
        }
    }

    public abstract Color TitleColour { get; }
}

With a nice method using reflection to fill a dictionary full of my Resources

I then extended this class with my actual theme:

public class MyTheme : Theme
{
    public MyTheme()
    {
        DictionaryThemes(typeof(MyTheme));
    }

    //Slate
    public override Color TitleColour { get; } = Color.FromHex("#00ff00");
}

Still with me? good...

I then had to load this theme into my Application Resources and merge it with my other application resources. I have to split the MyTheme resource from the other Resources so that I can use it in my main Resources file later.

Let me show you, here is my CurrentTheme resource file:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:styling="clr-MyApp.Styling;assembly=MyApp"
             x:Class="MyApp.Styling.CurrentTheme">
  <ContentPage.Resources>
    <ResourceDictionary>
  <styling:MyTheme x:Key="CurrentTheme"/>
</ResourceDictionary>

I have to do it as part of a page as Xamarin.Forms has Sealed the ResourceDictionary class (see here) meaning you can't extend it and create your own.. Shame. Anyway I digress.

I then set my application Resources to CurrentTheme like so:

 var currentTheme = new CurrentTheme();
 Xamarin.Forms.Application.Current.Resources = currentTheme.Resources;

I can then merge in other styles from my main Resource file, in this case called Styles:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:styling="clr-MyApp.Styling;assembly=MyApp"
             x:Class="MyApp.Styling.Styles">
  <ContentPage.Resources>
    <ResourceDictionary>
       <Style x:Key="TitleStyle" TargetType="Label">
          <Setter Property="FontAttributes" Value="Bold" />
          <Setter Property="FontSize" Value="30" />
          <Setter Property="TextColor" Value="{styling:Colour TitleColour}" />
       </Style>
</ResourceDictionary>

I merge this into my main application resources like so:

 var styles = new Styles();
 foreach (var item in styles.Resources)
 {
       Application.Current.Resources.Add(item.Key,item.Value);
 }

Now as you can see one of the setter properties in the Styles class looks like so:

and you're saying whats that value mean?

Here comes the final piece of the puzzle. An extension method that allows you to define your xaml like the above.

Firstly the ColourExtension class:

// You exclude the 'Extension' suffix when using in Xaml markup
[ContentProperty("Text")]
public class ColourExtension : IMarkupExtension
{
    public string Text { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        if (Text == null)
            throw new Exception("No Colour Key Provided");

        return StylingLookup.GetColour(Text);
    }
}

and finally the StylingLookup class:

public static class StylingLookup
{
    public static Color GetColour(string key)
    {
        var currentTheme = (Application.Current.Resources["CurrentTheme"] as Theme);

        return currentTheme.Colours[key];
    }
}

And now it all makes sense, Why we had to split CurrentTheme from the main Styles resources. Because of the line:

var currentTheme = (Application.Current.Resources["CurrentTheme"] as Theme);

If anyone has a better pattern for Styling an application I'd love to hear it

JKennedy
  • 18,150
  • 17
  • 114
  • 198
  • 1
    This has become a lot easier since `MergedDictionaries` were introduces in `Xamarin.Forms` this method can be simplified – JKennedy Sep 15 '16 at 09:32
0

@Arconox has the correct answer and here are a few extra tips.

public class ReferenceColor
{
    public Color Color { get; set; }

    public ReferenceColor()
    {

    }

    public static implicit operator Color(ReferenceColor referenceColor) => referenceColor.Color;
}

Is the code I am using for allowing myself to theme my Xamarin.Forms application at runtime. So to Arconox's point, INotifyPropertyChanged is not required unless you are changing themes/colors after application is running. This allows the use case like this.

<Color x:Key="Merlot">#750411</Color>
<local:ReferenceColor x:Key="Page.Static.Background" Color="{StaticResource Merlot}"/>
<Grid BackgroundColor="{DynamicResource Page.Static.Background}">
</Grid>
ivie
  • 61
  • 3