Strategies for sharing XAML code in Universal Apps (2/2)

This is the second part of the article, you can find the first one here : Strategies for sharing XAML code in Universal Apps (1/2) 

We will see different options to get UI and functionnal platform specificities while sharing some XAML at the same time:

  • Styles
  • DataTemplates
  • UserControl
  • Partial Class
  • From shared to specific to shared again
  • #if WINDOWS_PHONE_APP / WINDOWS_APP

 

You can download the source code of this basic sample app here

Going Platform specific

Ok, until here we still share all the XAML code. But what if want to be even more specific on each platform ? Well, there are many possibilities.

Styles

Let’s say I want to have a different color for the photo Title on Windows and Windows Phone ? Well, you can put styles in the platform-specific projects and use them in a shared XAML file. In my case I will change the color of the photo title.

I will add a resource dictionary file called CustomDictionary.xaml on each Win and WP projects, and load them in the app.xaml common file.

 <Application
    x:Class="BuildMemories.App"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BuildMemories">

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="CustomDictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

image

I will use Pink in the Windows app and Yellow for Windows Phone (I know, I’m not good at choosing colors Sourire ).

BuildMemories.Windows/CustomDictionary.xaml:

 <ResourceDictionary
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BuildMemories">
    
    <Style x:Key="MonTextblock" TargetType="TextBlock">
            <Setter Property="Foreground" Value="DeepPink"></Setter>
    </Style>
</ResourceDictionary>

BuildMemories.WindowsPhone/CustomDictionary.xaml

 <ResourceDictionary
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BuildMemories">
    
    <Style x:Key="MonTextblock" TargetType="TextBlock">
            <Setter Property="Foreground" Value="Yellow"></Setter>
    </Style>
</ResourceDictionary>

I will now use this style in my shared MainPage.xaml

 <TextBlock TextWrapping="Wrap" Text="{Binding ElementName=fv, Path=SelectedItem.Title}" Margin="10" FontSize="28"
            Style="{StaticResource MonTextblock}"/>

image  image

DataTemplate

You can also use a specific DataTemplate on each Platform. I will do that for my FlipView, so that it shows different data on Windows and Windows Phone. To do so, I create 2 different DataTemplates called APhotoTemplate used by the FlipView ItemTemplate.

BuildMemories.Shared/MainPage.xaml

 <FlipView Margin="0,9,0,-9" ItemsSource="{Binding Items}" 
           ItemTemplate="{StaticResource APhotoTemplate}">

The Template APhotoTemplate will be defined in the resource dictionary just like the style. I will add the photo title on the top of the photo just for the Windows 8.1 version:

BuildMemories.Windows/CustomDictionary.xaml:

 <DataTemplate x:Name="APhotoTemplate">
    <Grid>
        <Image Source="{Binding Path}" VerticalAlignment="Top" />
        <TextBlock TextWrapping="Wrap" Text="{Binding Title}" FontSize="28" Margin="10"/>
    </Grid>
</DataTemplate>

BuildMemories.WindowsPhone/CustomDictionary.xaml:

     <DataTemplate x:Name="APhotoTemplate">
        <Grid>
            <Image Source="{Binding Path}" VerticalAlignment="Top" />
        </Grid>
    </DataTemplate>

image

UserControl

You can also have a specific UserControl : for the example, I will add a “comments” textbox only in the Windows 8.1 version. I will create a usercontrol for all the banner stuff, on each platform specific project and refer to it in the shared MainPage.xaml:

 <local:BannerUserControl x:Name="spActions" 
                          DataContext="{Binding ElementName=fv, Path=SelectedItem}"/>

BuildMemories.Windows/BannerUserControl.xaml:

 <UserControl
    x:Class="BuildMemories.BannerUserControl"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:BuildMemories"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    x:Name="uc">
    <StackPanel Orientation="{Binding ElementName=uc, Path=Orientation}" VerticalAlignment="Bottom" 
                Background="{ThemeResource SystemColorControlAccentBrush}" Opacity="0.8">

        <TextBlock TextWrapping="Wrap" Text="{Binding Title}" Margin="10" FontSize="28"
                   Style="{StaticResource MonTextblock}"/>
        <DatePicker Margin="10"/>
        <CheckBox Content="I like that" Margin="10"/>
        <TextBox Width="300" Margin="10"></TextBox>
        <Button Content="Share" Margin="0"/>
    </StackPanel>
</UserControl>

BuildMemories.WindowsPhone/BannerUserControl.xaml:

Same code than in the Windows version, but without the highlighted TextBox used for the comments.

Partial class

To get the benefit of my Comment textbox, which is specific to my Windows version, I will need to bind it to my ViewModel. But this one is in the shared project and I don’t want to add Windows specific stuff in it.

So, want I can do is add platform specific behavior to my ItemViewModel class, thanks to the fact that it is partial. Let’s add a new file in the Windows project, called ItemViewModel.cs and add the comment stuff in it:

 using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Text;

namespace BuildMemories.ViewModels
{
    // Using a partial class will help sharing VM accross shared and platform specific projects
    public partial class ItemVM : ViewModelBase
    {
        private string _comments;
        public string Comments
        {
            get
            {
                return _comments;
            }

            set
            {
                Set(() => Comments, ref _comments, value);
            }
        }
    }
}

Do the binding in the xaml file:

 <TextBox Width="300" Margin="10" 
            Text="{Binding Comments, Mode=TwoWay}"/>

image

The Windows Phone app didn’t change:

image

From shared to specific to shared again

Our usercontrol is platform-specific, but we still need common business functionnalities, for the Share button for instance. Even if the layout is different, sharing will remain the same.

Well, you can put the code for the share command in the ViewModel of the Shared project.

This means that we have a Shared XAML MainPage using a specific (non shared) UserControl, which itself uses a shared Command of the shared ItemViewModel.

That would not always be the best choice to make, but maybe sometimes it is so it’s good to know it’s possible.

Just add the command to the ItemVM is the shared project:

 // Using a partial class will help sharing VM accross shared and platform specific projects
public partial class ItemVM : ViewModelBase
{
    public string Path { get; set; }
    public string Title { get; set; }
        
    RelayCommand _shareItCmd;

    public RelayCommand ShareCmd
    {
        get
        {
            return _shareItCmd ?? (_shareItCmd = new RelayCommand(ShareIt));
        }
    }

    async void ShareIt()
    {
        var msg = new MessageDialog("Shared !");
        await msg.ShowAsync();
    }
}

And simply bind the command in the BannerUserControl.xaml in both Windows and Windows Phone project.

 <Button Content="Share" Command="{Binding ShareCmd}"/>

You can see that for the same MessageDialog class, we get a nice specific UX in each Windows and Windows Phone app.

imageimage

#if WINDOWS_PHONE_APP / WINDOWS_APP

As you share files and not binaries, you can also use pre-compilation directives in you C# code in the shared project. This can be a good alternative to avoid creating specific code files in the Platform projects, especially when the specificities are very small.

Example : handling the back button on Windows Phone.

 #if WINDOWS_PHONE_APP
            Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
#endif

For Windows 8.1 specific, you would use WINDOWS__APP.

Conclusion

The option of sharing XAML in universal apps is very exciting and gives us much more options for organizing our code in cross-platform applications…but it doesn’t mean that you should do it !

Don’t think I encourage you to share the maximum percentage of your XAML code : it can be very tricky and difficult to undestand what styles, datatemplates and usercontrols are shared or not, and how they relate to each other.

But I wanted to show you an overview of the main technical possibilities for sharing xaml, to help you do your own choices.

You can download the source code of this basic sample here.