Three ways to set specific DeviceFamily XAML Views in UWP

Three ways to set specific DeviceFamily XAML Views in UWP

The new Universal Windows Platform brings a new feature known as DeviceFamily specific views which allows developers to define specific XAML views for specific device families (Desktop, Mobile, Team, IoT etc.). This can be really useful if you want to have major differences in your UI for different device families. Yes, RelativePanel is nice, and so are the VisualStateTriggers, but XAML can get really huge if you want to make major changes. This way we can define a completely specific XAML and share the code behind.

There are three ways to set specific device family XAML view. Let's just assume that we have a simple MainPage in the project, with blue background and some text, and we want specific XAML views for a mobile device family, with different background colors and different text.

<Page  
    x:Class="DeviceFamily.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DeviceFamily"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Blue">
        <TextBlock Foreground="White" Text="This is desktop." FontSize="72"
                   VerticalAlignment="Center" HorizontalAlignment="Center" />
    </Grid>
</Page>  

Let's pretend that it's a very complex layout and we want to make a new one for mobile device family. :)

DeviceFamily-Type folder

The most common way to accomplish this is to specify a new folder in your project, called DeviceFamily-Type, where Type is the name of the device family type (Mobile, Team, Desktop, IoT). In our case, that would mean creating a new folder called DeviceFamily-Mobile.

DeviceFamily-Mobile folder

The next step would be adding a XAML view called the same as the page (so in this case, MainPage.xaml)

DeviceFamily-Mobile new XAML View

This file won't have any code-behind, it will use the code-behind of the main MainPage.xaml.cs.

DeviceFamily-Mobile new structure

So, if you run the app on a mobile device (let's say in an emulator), it will load the XAML from DeviceFamily-Mobile/MainPage.xaml. For any other device family type, it will load the good old MainPage.xaml from the main folder.

This way of defining a new view is the most common. Other blog posts also covered it, such as this one: Creating DeviceFamily specific layouts in a Universal App.

.DeviceFamily-Type in file name

The second way you can accomplish the same thing is to create a new XAML View with, again, the same name, but with DeviceFamily-Type extension. So, for our MainPage, it would mean adding a new XAML view file called MainPage.DeviceFamily-Mobile.xaml to the same folder where the MainPage is.

DeviceFamily-Mobile in file name

Note that if you try using methods #1 and #2 at the same time, you will get a compile error: Error Processing Resources failed with error: Duplicate Entry.

And that's it - depending on the device family, one of the two XAML files will get loaded.

InitializeComponent overload

Interesting thing happens as soon as you add a new specific view to DeviceFamily-Type folder and compile - a new compiler generated overload of InitializeComponent shows up in MainPage.g.i.cs.

public void InitializeComponent(global::System.Uri resourceLocator)  
{
    if (_contentLoaded)
        return;

    _contentLoaded = true;

    if (resourceLocator == null)
    {
        resourceLocator = new global::System.Uri("ms-appx:///MainPage.xaml");
    }
    global::Windows.UI.Xaml.Application.LoadComponent(this, resourceLocator, global::Windows.UI.Xaml.Controls.Primitives.ComponentResourceLocation.Application);
}

The methods takes a Uri as parameter so you can specify exactly which XAML view you want to use. If you don't specify any, it will use the default for that page. Let's say you have a primary and secondary mobile MainPage view

DeviceFamily-Mobile with different views to choose from

You can use any kind of logic inside of page constructor to decide which one to use:

public MainPage()  
{
    if (AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Mobile")
    {
        if (usePrimary)
        {
            InitializeComponent(new Uri("ms-appx:///PrimaryMainPage.xaml", UriKind.Absolute));
        }
        else
        {
            InitializeComponent(new Uri("ms-appx:///SecondaryMainPage.xaml", UriKind.Absolute));
        }
    }
    else
    {
        InitializeComponent();
    }
}

So let's take a look at the results of using DeviceFamily specific view. First of all, there's the default (desktop) view, MainPage.xaml.

DeviceFamily default view (desktop)

If a simple method #1 or number #2 is used, with an alternative MainPage.xaml view for mobile device family (with a different background and text, to simplify things):

<Grid Background="Red">  
    <TextBlock Foreground="White" Text="This is mobile." FontSize="24"
               VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>  

then the emulator will load that XAML view:

DeviceFamily simple mobile view

But if method #3 is used, with different primary/secondary mobile views, then the results are (depending on which view is used in InitializeComponent) the following:

DeviceFamily primary mobile view

DeviceFamily secondary mobile view

Bonus - state triggers (single view)

This post is about creating completely separate XAML views. Still (as pointed out on Twitter), it's worth mentioning that in case that you want to have a single XAML view and alter things in that view based on DeviceFamily, it's really easy to do that using state triggers. You can write your own, or use the existing WindowsStateTriggers library.

WindowsStateTriggers DeviceFamily sample

Conclusion

DeviceFamily XAML view feature is a great way to make different, specific view for specific device families. This saves us the trouble of defining all in one huge XAML, and makes designing apps for various device families easier. Another great thing is that there are various ways you can use it in your app, even manually setting which view to use through InitializeComponent overload.

Complete code is on GitHub

Igor Ralic

igor ralic

View Comments
Microsoft Certified Solutions Developer: Windows Store Apps in C#