Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Getting Started with Xamarin.Forms and Platform Specific Resources using OnPlatform

Previous posts in this sequence on Getting Started with Xamarin.Forms:
Multi-Targeting in Visual Studio, SDK Versions, MvvmCross, Authenticating with ADAL
Refactoring MvvmCross and Services, Effects, Navigation with MvvmCross
Presentation Attributes in MvvmCross, Resources and Styles, Resource Dictionaries

In previous posts we’ve looked at using resources and then styles and resource dictionaries in order to manage the style of elements throughout our application. Whilst Xamarin.Forms provides an out-of-the-box cross platform experience, the reality is that if you don’t tweak the layout a bit for each platform, your application is going to look very generic, and not in a good way. In this post we’ll look at some of the different ways that you can adjust layout based on which target platform the application is running on.

The starting point is to look at an individual element, such as a Label, and how an individual property can be adjusted for different platforms using an OnPlatform element.

<Label Text="Hello World!">
     <Label.FontAttributes>
         <OnPlatform x:TypeArguments="FontAttributes">
             <On Platform="Android" Value="Italics"/>
             <On Platform="iOS" Value="None"/>
             <On Platform="UWP" Value="Bold"/>
         </OnPlatform>

     </Label.FontAttributes>
</Label>

In this example instead of supplying a value for the FontAttributes attribute we’ve expanded into long form using a Label.FontAttributes element. Nested within this element we create an OnPlatform element, supplying the TypeArguments attribute to indicate what Type of value the OnPlatform is going to produce. Within the OnPlatform element we then have an On element for each platform we want to specify a value for.

Since we’re not a big fan of having literals specified for an individual element, and even more so when doing per-platform styling, we can also define resources using the OnPlatform syntax:

<OnPlatform x:Key ="FeatureColor" x:TypeArguments="Color">
     <On Platform="Android" Value="Red"/>
     <On Platform="iOS" Value="Green"/>
     <On Platform="UWP"  Value="Blue"/>
</OnPlatform>

This defines a resource, FeatureColor, within a ResourceDictionary, with different colors defined for each of the three platforms.

As you can see, defining values for each platform can start to add a lot of bloat to your XAML. A work around for this is to use a similar technique that we’ve discussed previously where we include files on a per platform basis. For resources this requires a bit of fiddling, so there are a few steps involved. We need to start with the csproj for the UI project and adjust the first ItemsGroup to remove all XAML files that are within the Platforms folder. Next we need to selectively add back the XAML files based on what the target platform is. The bold lines in the following XAML from the csproj file indicate these changes.

<ItemGroup>
   <Compile Remove="Platforms\**\*.cs" />
   <None Include="Platforms\**\*.cs" />
   <EmbeddedResource Remove="Platforms\**\*.xaml" />
   <None Include="Platforms\**\*.xaml" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
   <Compile Include="Platforms\Netstandard\**\*.cs" />
   <EmbeddedResource Include="Platforms\Netstandard\**\*.xaml" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('uap')) ">
   <Compile Include="Platforms\Uap\**\*.cs" />
   <EmbeddedResource Include="Platforms\Uap\**\*.xaml" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
   <Compile Include="Platforms\Ios\**\*.cs" />
   <EmbeddedResource Include="Platforms\Ios\**\*.xaml" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid')) ">
   <Compile Include="Platforms\Android\**\*.cs" />
   <EmbeddedResource Include="Platforms\Android\**\*.xaml" />
</ItemGroup>

Next we need to create platform specific XAML files. I simply copied the LiteralResources.xaml and LiteralResources.xaml.cs into each of the Platform folders and renamed both the files and the class name to PlatformLiteralResources. In my case the project structure looks like the following:

image

You’ll notice that the PlatformLiteralResources.xaml is only showing under the Netstandard – this appears to be a bit of a bug in Visual Studio as the file exists and should appear in the project structure for all the platforms. The only thing left to do is define some platform resources and to link the PlatformLiteralResources class into the resource dictionary hierarchy:

PlatformLiteralResources

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="
http://xamarin.com/schemas/2014/forms"
                     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                     xmlns:res="clr-namespace:StrataPark.UI.Resources"
                      x:Class="StrataPark.UI.Resources.PlatformLiteralResources">
     <ResourceDictionary.MergedDictionaries>
         <res:LiteralResources />
     </ResourceDictionary.MergedDictionaries>
    <x:Double x:Key="DefaultLabelFontSize">24</x:Double>
</ResourceDictionary>

StyleAndTemplateResources

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="
http://xamarin.com/schemas/2014/forms"
                     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                     xmlns:res="clr-namespace:StrataPark.UI.Resources"
                     x:Class="StrataPark.UI.Resources.StyleAndTemplateResources">
     <ResourceDictionary.MergedDictionaries>
         <res:PlatformLiteralResources />
     </ResourceDictionary.MergedDictionaries>

    <!-- Other resources -->
</ResourceDictionary>

Note that the hierarchy is now App.xaml >> StylesAndTemplateResources >> PlatformLiteralResources >> LiteralResources.

And there you have it, you can now define platform specific literals in the PlatformLiteralResources xaml file without worrying about using the OnPlatform element.

Getting Started with Xamarin.Forms and Resource Dictionaries

Previous posts in this sequence on Getting Started with Xamarin.Forms:
Multi-Targeting in Visual Studio, SDK Versions, MvvmCross, Authenticating with ADAL
Refactoring MvvmCross and Services, Effects, Navigation with MvvmCross
Presentation Attributes in MvvmCross, Resources and Styles

Last post, on Resources and Styles, we discussed how resources, such as styles, can be defined at different levels within the view hierarchy. In this post we’re going to cover using resource dictionaries to better manage resources. Let’s start by recapping how resources can be defined by starting with a basic application level resource, which are resources that can be accessed anywhere in the application.

<Application.Resources>
     <Thickness x:Key="LeftRightMargin">48,0</Thickness>

As we go down the view hierarchy, we can define resources at a page level, which are resources that will only be accessible to elements on the same page

<Page.Resources>
     <Thickness x:Key="LeftRightMargin">36,0</Thickness>

A couple of things to note here – firstly we’re defining a resource with the same name as at the application level, which means we’re going to be overriding the resource value but only for this page. It’s also worth nothing that we’re using Page.Resources, despite the type of the page being ContentPage (for a normal Xamarin.Forms page) or MvxContentPage (for an MvvmCross page). This is acceptable since Page is the base class for both ContentPage and MvxContentPage, meaning that we can use it to reference the Resources property. It’s up to you whether you use Page.Resources or ContentPage.Resources, or even MvxContentPage.Resources, as they all reference the same property.

We can continue down the hierarchy, defining resources at either the container, or element level. As before, the resources are only accessible to the element and it’s children where the resource is defined.

<Grid.Resources>
     <Thickness x:Key="LeftRightMargin">24,0</Thickness>

<Label.Resources>
     <Thickness x:Key="LeftRightMargin">12,0</Thickness>

Now that we've seen how we can define resources at any level in the view hierarchy but currently we’re storing all our application level resources into the App.xaml, which overtime is going to get quite hard to manage. One approach to make resources easier to manage is to create separate resource dictionaries for different types of resources. My preference is to have a dictionary that contains literals such a colours, strings, Thickness, converters, and another resource dictionary that has styles and templates.

Let’s start by creating a separate dictionary and linking it to our App.xaml. Unfortunately Visual Studio doesn’t currently come with an item template for a ResourceDictionary for Xamarin.Forms. I’m going to create a folder called Resources within the UI project, open the folder in File Explorer and create a new file called StyleAndTemplateResources.xaml.

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="
http://xamarin.com/schemas/2014/forms"
                     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">

    <!-- Add all Style and Template resources here -->
    <Style TargetType="Label"  x:Key="SomeLabelStyle" />
</ResourceDictionary>

This needs to be referenced in App.xaml to ensure the resources are loaded at application startup.

<Application.Resources>
     <ResourceDictionary Source="Resources\StyleAndTemplateResources.xaml"/>
</Application.Resources>

Note that we’re using the new syntax supported in Xamarin.Forms v3+ where we do not need to include the MergedDictionaries tag and we can load the ResourceDictionary based on the path to the XAML. This means that we don’t need to specify the Class attribute in the XAML for the ResourceDictionary; nor do we need the code behind file.

Let’s also add another ResourceDictionary which will contain our literals. This will follow the same structure as the StyleAndTemplateResources.xaml but called LiteralResources.xaml, and we need to update the App.xaml.

<Application.Resources>
    <!-- This will NOT work -->
     <ResourceDictionary Source="Resources\LiteralResources.xaml"/>
     <ResourceDictionary Source="Resources\StyleAndTemplateResources.xaml"/>
</Application.Resources>

As the comment in the XAML states, adding the two resource dictionaries in parallel like this in the App.xaml will not work. At runtime you can expect a XAML parsing error, stating that resources defined in LiteralResources can’t be found when loading resources in StyleAndTemplateResources.

image

This is because both resource dictionaries are loaded independently and then merged into the resource dictionary for the application. In order to reference resources from LiteralResources, it needs to be loaded first, followed by StyleAndTemplateResources. You should be able to do this by chaining resource dictionaries as follows (Warning: this currently does not work!):

App.xaml

<Application.Resources>
    <ResourceDictionary Source="Resources\StyleAndTemplateResources.xaml"/>
< /Application.Resources>

StyleAndTemplateResources

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
     <ResourceDictionary.MergedDictionaries>
         <ResourceDictionary Source="Resources\LiteralResources.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <!-- Other resources -->
</ResourceDictionary>

So, the question is – if this doesn’t work, what do you have to do? Well the good news is that we’re on the right track using a hierarchy of dictionaries. Unfortunately due to some issues with Xamarin.Forms, we can’t use the simplified form, instead we need to make sure that each resource dictionary has a Class attribute and a code behind file, and we need to explicitly use the MergedDictionaries element.

Start by creating code behind files for both resource dictionaries:

public partial class LiteralResources
{
     public LiteralResources()
     {
         InitializeComponent();
     }
}
public partial class StyleAndTemplateResources
{
     public StyleAndTemplateResources()
     {
         InitializeComponent();
     }
}

Next, we’ll update App.xaml and the other resource xaml files to define the Class attribute and fix the hierarchy:

LiteralResources.xaml

<?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="StrataPark.UI.Resources.LiteralResources">
     <Thickness x:Key="LeftRightMargin">48,0</Thickness>

    <x:Double x:Key="DefaultLabelFontSize">24</x:Double>
     <Color x:Key="FeatureColor">Green</Color>
</ResourceDictionary>

StyleAndTemplateResources.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="
http://xamarin.com/schemas/2014/forms"
                     xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                     xmlns:res="clr-namespace:StrataPark.UI.Resources"
                     x:Class="StrataPark.UI.Resources.StyleAndTemplateResources">
     <ResourceDictionary.MergedDictionaries>
         <res:LiteralResources />
     </ResourceDictionary.MergedDictionaries>

    <Style x:Key="FeatureLabelStyle" TargetType="Label">
         <Setter Property="TextColor" Value="{StaticResource FeatureColor}"/>
         <Setter Property="FontSize" Value="{StaticResource DefaultLabelFontSize}"/>
     </Style>

    <Style TargetType="Label" BasedOn="{StaticResource FeatureLabelStyle}"/>
</ResourceDictionary>

App.xaml

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="
http://xamarin.com/schemas/2014/forms"
              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
              xmlns:res="clr-namespace:StrataPark.UI.Resources"
              x:Class="StrataPark.UI.App">
     <Application.Resources>
         <ResourceDictionary>
             <ResourceDictionary.MergedDictionaries>
                 <res:StyleAndTemplateResources />
             </ResourceDictionary.MergedDictionaries>
         </ResourceDictionary>
    </Application.Resources>
</Application>

And there you have it – resources are separated out for easier maintainability.

Getting Started with Xamarin.Forms with Resources and Styles

Previous posts in this sequence on Getting Started with Xamarin.Forms:
Multi-Targeting in Visual Studio, SDK Versions, MvvmCross, Authenticating with ADAL
Refactoring MvvmCross and Services, Effects, Navigation with MvvmCross
Presentation Attributes in MvvmCross

In this post we’re going to look at how you can adjust the default style for the controls that you use within your Xamarin.Forms application. To get started, let’s look at how you might change the colour of a basic Label – there are a couple of options but all start with setting the TextColor property:

<Label TextColor="#0F0" />
<Label TextColor="#00FF00" />
<Label TextColor="Green" />

A lot of applications will have all sorts of layout properties set explicitly in the page XAML. Unfortunately this becomes increasingly hard to manage as the application grows. Imagine having 10+ pages all with the colour of elements set explicitly and then you want to change the theme of the application – this would require going through every element to make sure every colour is correct.

An alternative is to extract various properties into static resources where they can be managed at an application, page or even control level. Let’s look at defining an application resource in the App.xaml file in the UI project.

<Application xmlns="http://xamarin.com/schemas/2014/forms"
              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
              x:Class="StrataPark.UI.App">
     <Application.Resources>
         <Color x:Key="FeatureColor">Green</Color>
    </Application.Resources>
</Application>

In this case we’ve defined a Color resource with a Key of FeatureColor. This can be used anywhere in the application using the Key.

<Label TextColor="{StaticResource FeatureColor}" />

Extract properties into resources can be done for all sorts of values such as strings, colours, margins etc. Not all resources need to be defined at an application level. Resources can be added to the Resources dictionary of a Page or even a control (for example a Grid) – this limits the scope of that resource to the element where the resource is defined, and it’s children. For example, adding a resource to the Resources dictionary of a Page means that the resource is available to all elements on that page but not to elements on other pages within the application.

Note: It may seem simpler to add all resources to the Application Resources dictionary – just be aware that there is a performance cost associated with this as all resources will need to be loaded when the application is started up. Depending on the number and complexity of the resources, this can have a negative impact on the startup time for your application.

Returning to the example of setting the colour of the text of a Label, creating the FeatureColor resource makes it easy to apply the same colour to a number of Label elements and then to be able to change their colour in one place. However, there are two issues here:

- What if we want to be able to set other common properties across all these Label elements? For example, we might want to set FontSize.

- We still have to explicitly set the TextColor on each Label – it would be great if the colour could be set automatically on all Label elements.

The answer to both of these is to define a Style, which you can think of as a collection of property setters, applied by setting the Style property of an element. Let’s start with the first issue where we want to be able to set the FontSize as well as the colour. To do this we’ll create a Style called FeatureLabelStyle, again in the App.xaml so it can be used throughout the application.

<x:Double x:Key="DefaultLabelFontSize">24</x:Double>
<Color x:Key="FeatureColor">Green</Color>

<Style x:Key="FeatureLabelStyle" TargetType="Label">
     <Setter Property="TextColor" Value="{StaticResource FeatureColor}"/>
     <Setter Property="FontSize" Value="{StaticResource DefaultLabelFontSize}"/>
</Style>

Note that we still have a resource called FeatureColor which specifies the colour of the Label, and we have another resource that defines the size to be used for the FontSize. These are combined into the FeatureLabelStyle, which can then be applied by setting the Style property.

<Label Style="{StaticResource FeatureLabelStyle}" />

Moving now to the second issue, which is how we can apply a Style to all Label elements. Currently the Style we have defined has a Key assigned to it, making it an explicit Style, since the Key has to be used in order to explicitly specify the Style. In order to apply it to all elements we need to define an implicit style, a Style with no Key.

<Style TargetType="Label" BasedOn="{StaticResource FeatureLabelStyle}"/>

The implicit Style will be automatically applied by default to all Label elements. Note that we have based the implicit Style on the previously defined explicit style – this is best practice as it means we can take advantage of Style inheritance. For example you might have a base explicit Style that defined the default colour for all Label styles.

And there you have it, a quick overview of using Resources and Styles within your Xamarin.Forms application.