Building adaptive layout in Windows 10 with RelativePanel and AdaptiveTrigger

.NET, Technical stuff, Windows 10, Work, XAML
See comments

Windows 10 comes with a new class of applications named Universal Application Platform (UAP). It may sound like an old story because Universal apps had been introduced in Windows 8 already, but in fact the new UAP in Windows 10 is much more exciting.

I have shown how to create a new Windows 10 universal application with MVVM Light support and run it on various devices. In this article, I want to go a bit deeper on what Windows 10 universal apps are really, and how to handle various form factors and device families.

Disclaimer: There seems to be a known issue in Windows 10 Build 10061 preventing the code below to run (an exception is thrown when the VisualStateManager switches state). The Windows team confirmed that it is a known bug, fixed in future versions.

Difference to Windows 8 universal apps

The name “universal app” was a little bit of a marketing trick in Windows 8. In fact, you were creating 2 separate applications (Windows Store and Windows Phone) and a shared project. Every file copied in the Shared project would be made available to both applications: XAML pages and source code, resource files such as texts or images, audio files, etc.

In fact we had been creating this kind of apps earlier, in a more manual way: You can always add a file to a project (saw Windows Presentation Foundation) and add a shortcut to this file to another project (for example a companion Windows Phone application). The build process will take care of adding the linked file to each application that requires it.

Of course it was not super comfortable: you had to manually add the file in question to each project, and in case the file had to be renamed, for instance, this required quite a few manual steps. In this regard, the Windows 8 “universal app” was mostly about providing a more comfortable tooling system to support these operations.

In the end however, you still ended up with one app per device family. Sharing the code was done with either the linked files as explained, or with portable class libraries. Nothing was really new here.

The “real” universal apps

In Windows 10 however, you can create a “real” universal app, i.e an application that has only one executable file (package), but can be deployed to Windows Phone, Windows desktop, Windows tablets and even Xbox, Internet-of-things devices or HoloLens, the new augmented reality visor that Microsoft announced earlier and will present at the Build conference in a week.

For the developer, it means that the code doesn’t need to be shared anymore! In fact the code is now developed exactly once, and compiled exactly once, and presto it runs on all these devices. You create ONE application, which greatly reduces the maintenance effort. Of course handling the various form factors still require some effort: you cannot expect users of a phone to accept the same user experience than users of a powerful desktop computer. While the underlying code will be the same, the user interface needs to be adapted to each platform. Thankfully, Microsoft is providing us with a nice toolbox to handle pretty much every possible case.

Adapting the XAML

In Windows Phone or Windows Store apps, you might have already used the VisualStateManager (VSM) to handle the orientation of the device. This can be used to create a different layout in portrait or landscape mode. Similarly, we were also often using the VSM to handle split mode versus full screen mode in Windows Store apps. The user would grip the bar between two apps, move it and when a certain threshold was reached, the new state would kick in and controls were moved around, hidden and shown, etc. For example, we typically use a GridView in full screen mode (with horizontal scrolling) and a ListView (vertical) in split mode when the layout is narrow.

In Windows 10, a similar technique is possible but it has been improved.

The first improvement is that you can now define triggers for your UI. No it’s not the good old Windows Presentation Foundation triggers, but the purpose is similar. Another great improvement is that the VSM now supports Setters instead of having to create an animation for each single state. While it is still possible to transition between states with an animation, the need to create a 0-second animation for static state changes is now obsolete. This produces a much cleaner XAML markup.

For example, create a new Windows 10 universal application and open the MainPage.xaml. Then add the following XAML markup:

<Grid x:Name="LayoutRoot">
            <VisualState x:Name="WideState">
                    <AdaptiveTrigger MinWindowWidth="600" />

                    <Setter Target="LayoutRoot.Background"
                            Value="Green" />

            <VisualState x:Name="NarrowState">
                    <AdaptiveTrigger MinWindowWidth="0" />

                    <Setter Target="LayoutRoot.Background"
                            Value="Red" />

As you can see on line 2, we define the VisualStateManager’s VisualStateGroups. On line 4 we add a new state named WideState, with a trigger named AdaptiveTrigger on line 6. This trigger will kick in when the window is 600 pixels or larger, and the corresponding WideState will be applied.

On line 15, we define another state named NarrowState, and use another AdaptiveTrigger. Here, we specify that this trigger applies for sizes larger than 0 pixel (in effect, it means that the NarrowState will apply between 0 and 600 pixels).

At this point, the AdaptiveTrigger is the only available trigger for Windows 10. However nothing prevents you from creating your own triggers, like my good friend Morten Nielsen shows in his article “Using Custom Visual State Triggers”. For example you can think of a trigger for the device orientation, the pixel depth (DPI), the physical screen size, etc.

If you run the code now, you will see that the change between NarrowState and WideState is seamless, i.e it appears while the user is resizing the window, not only after he lets the mouse cursor go like in Windows 8. This is an obvious improvement in the user experience.

Combining VSM and RelativePanel

Often it is interesting to adapt the layout of your UI depending on the window’s width (or other parameters). We could do that using a Grid (probably the most versatile panel available), but here I would like to introduce a new panel named RelativePanel. This will sound very familiar to Android developers where the RelativeLayout panel is super useful. But in fact the Windows 10 RelativePanel’s real power is even more obvious when coupled to the VSM.

For example, let’s create the following UI when the window is larger than 600 px:

Screenshot (12)

On the other hand, if the windows is smaller than 600 px, we want the elements to appear vertically, as shown here:

Screenshot (17)

To do this, we will start by adding a RelativePanel to our main Grid as shown here:

<RelativePanel HorizontalAlignment="Stretch"

    <TextBlock Text="First name"

    <TextBox x:Name="FirstNameText"
             Width="300" />


Notice that in the code snippet above, I have not defined any relative position for the elements. If you were to run the code now, you would see only the TextBox because it appears on top of the TextBlock. As such, the RelativePanel acts as a Grid.

In the VSM, we can now extend the existing markup to specify where the UI elements must appear in the WideState:

<VisualState x:Name="WideState">
        <AdaptiveTrigger MinWindowWidth="600" />

        <Setter Target="LayoutRoot.Background"
                Value="Green" />
        <Setter Target="FirstNameText.(RelativePanel.RightOf)"
                Value="FirstNameLabel" />                        

In the NarrowState however, we want the TextBox to appear below the TextBlock.

<VisualState x:Name="NarrowState">
        <AdaptiveTrigger MinWindowWidth="0" />

        <Setter Target="LayoutRoot.Background"
                Value="Red" />
        <Setter Target="FirstNameText.(RelativePanel.Below)"
                Value="FirstNameLabel" />

Note the usage of parenthesis around the “RelativePanel.RightOf” and “RelativePanel.Below” properties. This is needed because RightOf and Below are attached properties defined by the RelativePanel class. This is similar to the well known Grid.Column/Grid.Row properties, or Canvas.Top/Canvas.Left, etc.

Running the code now will show the UI rendered like we wanted.


Building a completely new UI for a device family

The technique shown above is very nice, but in time it can lead to messy XAML with many different states, and this can be confusing. This is why Microsoft also introduces a new concept in Windows 10: You can now create multiple XAML files targeting the same code-behind. Again, this is not a new technique per se: In Android, it is already possible to create multiple UI markup files, and to select the correct one at runtime, based on screen size, screen depth, form factor etc. In Windows it is a little easier because it is convention-based.

To illustrate this, let’s start by running the application above on a Windows Phone device. Note that you cannot at this time run on a physical device, so we will use the emulator instead: In VS15, select the “run” button and choose one of the Emulators from the dropdown list.


As expected, the Red background appears, because the window’s width is smaller than 600 pixels. This is the same experience than on a Windows 10 tablet when the window is resized.

Now, follow the steps:

  • In the Solution Explorer, create a new folder and name it DeviceFamily-Mobile.
  • Right click on the folder, and select Add, New Item.
  • From the Add new item dialog, select XAML View. Name the new view just like the one that you want to replace. In our case, we must name it MainPage.xaml

This will add a new XAML page. There is one big difference however: This new XAML view does not have a code-behind file (MainPage.xaml.cs). If you open the XAML markup, you will see that it is specified that the new XAML must use the same MainPage.xaml.cs that we had created in the first place.

To prove that the same code-behind is used, let’s add a button to our XAML and an event handler.

  • In the DeviceFamily-Mobile/MainPage.xaml, add the following XAML markup:
<Grid Background="Blue">

    <Button Content="Proudly running on Windows Phone"
            Click="Button_Click" />

  • Then open the MainPage.xaml located in the project root. Here, add the following markup just below the RelativePanel:
<Button Content="And now for something totally different"
  • Finally let’s implement the event handler: Open MainPage.xaml.cs and add the following code:
private async void Button_Click(
        object sender, 
        RoutedEventArgs e)
    var dialog = new MessageDialog(
           "Hello " + FirstNameText.Text);
    await dialog.ShowAsync();

Now let’s run the application on the Windows Phone emulator again. You should see the following UI shown. Pressing the button will display a Windows Phone dialog, which looks like an extended toast notification.


Then, select “Local Machine” from the “run” dropdown menu.


Running the code now will show the well known Red/Green UI with a new button. Clicking this button will also display a dialog with the same text, but this time the Windows 10 dialog is used, which looks more like a message window:


At this stage, we can only speculate what DeviceFamily will be supported. It is reasonable to think that Microsoft will extend this concept to XBox and IOT devices, and even to HoloLens devices!

We will still be sharing…

Of course all the sharing techniques we learned in previous versions of Windows are not going to be disappearing any time soon.

First, we will continue to share code to other non-Windows 10 platforms such as Xamarin.iOS, Xamarin.Android or Xamarin.Forms. Portable class libraries or linked files are techniques that work great to create not only cross-device applications on Windows 10, but also cross-platform apps.

Then, of course, it is very possible that a Windows 10 UAP doesn’t cut it with a universal application, and that a different package needs to be created for a specific device family. This is of course always possible, and when you publish your app to the unified Windows store, you can specify which device family the application will be made available for. For these (probably rare) scenarios, the “old school” sharing techniques will still be perfectly valid.

I hope that this article helped you to understand better what Windows 10 universal applications are, and what techniques we will be using to create adaptive layout in our super rich applications. It’s a great satisfaction to see “truly universal apps” coming to Windows devices, and the mind can only dream of all the devices on which Windows (and your applications) will be running soon!

Happy coding

Previous entry | Next blog entry

Responses to “Building adaptive layout in Windows 10 with RelativePanel and AdaptiveTrigger”

  1. Thomas Levesque Says:

    Great article, thanks Laurent!

  2. Andrea Romeo Says:

    Great article thanks

  3. Saurabh Says:

    Great article. I had come to know abut relative panel, but the multiple xaml sharing same code behind technique was a bonus!

  4. Meenakshi Rana Says:

    Very useful..awesome

  5. Windows 10 UWA( UWP APP) 開發入門 (附範例) - hermanwu - Site Home - MSDN Blogs Says:

    […]  Building adaptive layout in Windows 10 with RelativePanel and AdaptiveTrigger […]

  6. Dan Says:

    When I tried the “Combine VSM with RelativePanel” part with setting rightof, it stayed Below the NameLabel and doesn’t seem to go up like your example shows. Can’t seem to use “x:null” to set it off either.

  7. lbugnion Says:

    Weird. I will try.

  8. Alfian Says:

    I cannot make this work, neither in VS 2015 community, nor enterprise. I have created folder DeviceFamily-Mobile and the XAML page with the same name, but it does not work. Is there any additional setting needed?

  9. Ray Says:

    Is there any way to evenly space the children in a RelativePanel?

  10. lbugnion Says:

    Hi, RelativePanel itself will not do spacing between elements, but like all panels, it does support children of type Panel. So maybe you can achieve what you want with a RelativePanel hosting a Grid, for example.

  11. Using DeviceFamily specific views with Caliburn.Micro - Bogdan Bujdea Says:

    […] was reading an article about building adaptive layout in Windows 10 with RelativePanel and AdaptiveTrigger, and one of the topics was about device family specific views. I was using a Caliburn.Micro […]

  12. Windows 10, the UWP, an old Windows 8.1 library and controlling Sphero across PC, Phone and Raspberry PI 2. - Mike Taulty's Blog - Mike Taulty's Blog Says:

    […] playing with this very simple UI I made use of RelativePanel in a single place which is to deal with the transition between […]

  13. Mateusz Says:

    How many VisualStates can be set? Is it limited to two? Or can I have 3 of them?

  14. lbugnion Says:

    As many as you want.

  15. Robert Iagar Says:

    Quick question. I’m trying to set up states in different pages using the NavPane template. In the AppShell there is a VSM setting some values. If I try to setup the VSM in a different Page and load that into the Frame, it’s not working. Not sure why.

  16. lbugnion Says:

    Do you have code to send? as far as i know VSM must be attached to the root element of the page

  17. Wietse Says:

    The visual state manager seems to work perfect, however it’s useless. When I place the RelativePanel, or Grid with the Visual State Manager in a Pivot or on the same level from another XAML element it totally destroys your layout…

    Don’t know what to do, but this is useless.

Comments for Building adaptive layout in Windows 10 with RelativePanel and AdaptiveTrigger