Bruno Peres
Bruno Peres

Reputation: 16365

How to reuse styles defined in a parent ContentView in Xamarin.Forms

Fist, I have a component called FieldView defined in this way:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="CustomViews.Components.Fields.FieldView">

    <ContentView.Resources>
        <ResourceDictionary>
            <Style x:Key="LabelStyle" TargetType="Label">
                <Setter Property="FontSize" Value="20"></Setter>
        </ResourceDictionary>
    </ContentView.Resources>

    <StackLayout>
        <Label Text="Hello world" Style="{StaticResource LabelStyle}"></Label>
    </StackLayout>

</ContentView>

The FieldView component has a style named "LabelStyle". Obviously I can use this style inside FieldView component. But this component is a base component that will be inherited by other components, like TextFieldView, implemented as follow:

<?xml version="1.0" encoding="UTF-8"?>
<local:FieldView xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:GeoSapiens.Coletum.CustomViews.Components.Fields"
    x:Class="GeoSapiens.Coletum.CustomViews.Components.Fields.TextFieldView">

    <StackLayout>

        <!-- the style is not accessible here -->
        <Label Text="Hello from TextFieldView" Style="{StaticResource LabelStyle}"></Label>

    </StackLayout>

</local:FieldView>

The problem is that I cannot use the style defined in the FieldView inside the TextFieldView component.

Is there a way that I can reference styles defined in my FieldView inside TextFieldView component? That is: access a style defined in parent component. Should I use the code-behind file in any way?

Upvotes: 3

Views: 2160

Answers (3)

Ben
Ben

Reputation: 2995

Define a separate ResourceDictionary from a new Content Page and use Merged Resource Dictionaries:

<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="MyNamespace.MyStyles">
  <Style x:Key="LabelResDictStyle" TargetType="Label">
    <Setter Property="Text" Value="Hello from ResDictStyle" />
  </Style>
</ResourceDictionary>

Redefine the code behind:

namespace MyNamespace
{
  [XamlCompilation(XamlCompilationOptions.Compile)]
  public partial class MyStyles : ResourceDictionary
  {
    public MyStyles()
    {
        InitializeComponent();
    }
  }
}

The style in the separate resource dictionary can be referenced in the ContentView:

xmlns:resDictStyles="clr-namespace:MyNamespace"
...
<ContentView.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <resDictStyles:MyStyles />
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</ContentView.Resources>
...
<Label Style="{StaticResource LabelResDictStyle}" />

The style from the merged resource dictionary can be "extended" using BasedOn:

xmlns:resDictStyles="clr-namespace:MyNamespace"
...
<ContentPage.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <resDictStyles:MyStyles />
    </ResourceDictionary.MergedDictionaries>
    <Style x:Key="LabelPageStyle" TargetType="Label" BasedOn="{StaticResource LabelResDictStyle}">
      <Setter Property="FontSize" Value="20"></Setter>
    </Style>
  </ResourceDictionary>
</ContentPage.Resources>
...
<Label Style="{StaticResource LabelPageStyle}" />

Upvotes: 1

Diego Rafael Souza
Diego Rafael Souza

Reputation: 5313

Nick's answer really works, it's just a syntax mistake that I can't imagine how your app is running on with it...

However, you'll be able to extend the styles (and any other resource) if you create a method to provide the ResourceDictionary instead of defining it on XAML. So your base view will look like this:

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomViews.Components.Fields.FieldView">
    <StackLayout>
        <Label Text="Hello world" 
               Style="{StaticResource LabelStyle}"/>
    </StackLayout>
</ContentView>

And then, in the code behind you should implement and use the method to get the resources dictionary:

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FieldView : ContentView
{
    public FieldView()
    {
        InitializeComponent();
        // Here you set the Resources property through your method
        Resources = GetResources();
    }

    protected virtual ResourceDictionary GetResources()
    {
        ResourceDictionary ret = new ResourceDictionary();

        // Create your style here
        Style labelStyle = new Style(typeof(Label));
        labelStyle.Setters.Add(new Setter() { Property = Label.FontSizeProperty, Value = 20 });

        // Add the style to Resource Dictionary
        ret.Add("LabelStyle", labelStyle);

        // Return the resource Dictionary
        return ret;
    }
}

In your child view you should set the Resources property just like you did on base and add new resources as much as you need:

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ChildView : FieldView
{
    public ChildView()
    {
        InitializeComponent();
        // Call the same method
        Resources = GetResources();
    }

    protected override ResourceDictionary GetResources()
    {
        ResourceDictionary res = base.GetResources();

        // You can add new Styles here, for example
        Style newStyle = new Style(typeof(Button));
        newStyle.Setters.Add(new Setter() { Property = Button.BackgroundColorProperty, Value = Color.Red });

        res.Add("DangerButtonStyle", newStyle);

        return res;
    }
}

I hope it would be useful.

Upvotes: 1

Nick Peppers
Nick Peppers

Reputation: 3251

If you are using that same Style in multiple views throughout your entire app I would probably just move your style into your App.xaml and use it from there.

However, what you are trying to do should work if TextFieldView base class is set to FieldView but it looks like from your code your resource isn't properly defined in it and is missing a closing </Style>. For example, the code below worked when I tried it out and used TextFieldView in a xaml page.

FieldView:

<ContentView 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    x:Class="TestApp.Forms.FieldView">
    <ContentView.Resources>
        <ResourceDictionary>
            <Style x:Key="LabelStyle" TargetType="Label">
                <Setter Property="FontSize" Value="40" />
            </Style>
        </ResourceDictionary>
    </ContentView.Resources>
    <StackLayout>
        <Label Text="Hello world" Style="{StaticResource LabelStyle}" />
    </StackLayout>
</ContentView>

TextFieldView:

<local:FieldView 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:local="clr-namespace:NuclearHalfLife.Forms" 
    x:Class="TestApp.Forms.TextFieldView">
    <StackLayout>
        <Label Text="Hello from TextFieldView" Style="{StaticResource LabelStyle}" />
    </StackLayout>
</local:FieldView>

Upvotes: 2

Related Questions