Develop Different UI For Different Device Family In UWP

In this article I've tried to cover most of all the possible ways to develop different UI for different device families while making apps for Windows 10 UWP. There are couple of ways and we'll elaborate each of those possibilities here.

Using <VisualStateManager> in XAML

This method is more useful when the UI differences between a Windows phone and the desktop (other bigger devices) are not major. Because if the UI of the phone and desktop are entirely different then the code in the XAML file will be more complex and will be hard to maintain for future changes.
Let's take a simple example to understand this. In our project we have a page named MainPage. And in that we took a TextBlock to display the device name.

Here in VisualStateManager we're using AdaptiveTrigger for the app's  minimum windows width and through that we're judging if the app is running on phone or desktop.

Take a look at the code of our MainPage.xaml file.
  1. <Grid x:Name="RootGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">  
  2.     <VisualStateManager.VisualStateGroups>  
  3.         <VisualStateGroup>  
  4.             <VisualState x:Name="visualStateMobile">  
  5.                 <VisualState.StateTriggers>  
  6.                     <AdaptiveTrigger MinWindowWidth="0" />  
  7.                 </VisualState.StateTriggers>  
  8.                 <VisualState.Setters>  
  9.                     <Setter Target="tb.Text" Value="Mobile App" />  
  10.                 </VisualState.Setters>  
  11.             </VisualState>  
  12.             <VisualState x:Name="visualStateDesktop">  
  13.                 <VisualState.StateTriggers>  
  14.                     <AdaptiveTrigger MinWindowWidth="700" />  
  15.                 </VisualState.StateTriggers>  
  16.                 <VisualState.Setters>  
  17.                     <Setter Target="tb.Text" Value="Desktop App" />  
  18.                 </VisualState.Setters>  
  19.             </VisualState>  
  20.         </VisualStateGroup>  
  21.     </VisualStateManager.VisualStateGroups>  
  22.     <TextBlock x:Name="tb" Text="Desktop App" FontSize="36" HorizontalAlignment="Center" VerticalAlignment="Center" />  
  23. </Grid>  
VisualStateManager must always be put on the root grid of any specific page. Here's the output of running this page in desktop and phone.

Using DeviceFamily-Type Folder for each DeviceFamily

To makea  completely different UI for phone and desktop, you can use different XAML views for different DeviceFamily.

For that first of all create a folder in your project and name it as "DeviceFamily-type" here type is the DeviceFamily type you're making the design for i.e. Mobile, Desktop, IoT, Team etc.


To understand this way of creating UI we're going to build a sample in which the mobile UI will be majorly different then Desktop UI.

In UWP if any app is providing "Options," or say "Menu," then the place of that menu should be according to the device on which the app is running.
For example if the app is running on Windows phone then the menu should be in the bottom app bar. And if the app is running on a bigger device like a desktop or tablet then the menu should be in the SplitView Pane (Hamburger Menu). We'll build a sample for that.
  • Add a page named "MyPage.xaml" in your project which represents the default Page.

  • And then add one "XAML View" with the same name but this time add it to the folder we've created "DeviceFamily-Mobile". Remember here in the DeviceFamily-Mobile folder we're adding a "XAML View" instead of "Page".

  • So what happens here when the App is running on the Mobile Devices, It will show the XAML view UI from the "DeviceFamily-Mobile" folder and it'll share the Code behind of MyPage.xaml.cs Page we've added in the Project.


Code of MyPage.xaml View -> for Mobile view,
  1. <Page>   
  2.   x:Class="SampleApp.MyPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:SampleApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">  
  3.     <Page.BottomAppBar>  
  4.         <CommandBar>  
  5.             <AppBarButton x:Name="appbarBtnHome" Icon="Home" Label="Home" Click="appbarBtnHome_Click" />  
  6.             <AppBarButton x:Name="appbarBtnSettings" Label="Settings" Icon="Setting" Click="appbarBtnSettings_Click" />  
  7.             <CommandBar.SecondaryCommands>  
  8.                 <AppBarButton x:Name="appbarBtnAbout" Label="About" Icon="Import" />  
  9.                 <AppBarButton x:Name="appbarBtnFeedback" Label="Feedback" Icon="Mail" />  
  10.             </CommandBar.SecondaryCommands>  
  11.         </CommandBar>  
  12.     </Page.BottomAppBar>  
  13.     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">  
  14.         <TextBlock Text="Mobile App" FontSize="26" HorizontalAlignment="Center" VerticalAlignment="Center" />  
  15.     </Grid>  
  16. </Page>  
Code of MyPage.xaml Page -> for Desktop,
  1. <Page>  
  2.     x:Class="SampleApp.MyPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:SampleApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">  
  3.     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">  
  4.         <Grid.RowDefinitions>  
  5.             <RowDefinition Height="Auto" />  
  6.             <RowDefinition Height="*" />  
  7.         </Grid.RowDefinitions>  
  8.         <RelativePanel Background="#FFDEDEDE">  
  9.             <Button x:Name="btnHamburger" Content="" FontSize="36" FontFamily="Segoe MDL2 Assets" RelativePanel.AlignLeftWithPanel="True" Click="btnHamburger_Click" />  
  10.             <TextBlock Text="Desktop App" FontSize="26" RelativePanel.RightOf="btnHamburger" Margin="12 0 0 0" RelativePanel.AlignVerticalCenterWithPanel="True" />  
  11.         </RelativePanel>  
  12.         <SplitView x:Name="splitView" Grid.Row="1" OpenPaneLength="200">  
  13.             <SplitView.Pane>  
  14.                 <ListView>  
  15.                     <TextBlock Text="Home" FontSize="22" />  
  16.                     <TextBlock Text="Settings" FontSize="22" />  
  17.                     <TextBlock Text="Feedback" FontSize="22" />  
  18.                     <TextBlock Text="About" FontSize="22" />  
  19.                 </ListView>  
  20.             </SplitView.Pane>  
  21.             <SplitView.Content>  
  22.                 <TextBlock Text="This is Content" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="36" />  
  23.             </SplitView.Content>  
  24.         </SplitView>  
  25.     </Grid>  
  26. </Page>  
Code for shared MyPage.xaml.cs file,
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.IO;  
  4. using System.Linq;  
  5. using System.Runtime.InteropServices.WindowsRuntime;  
  6. using Windows.Foundation;  
  7. using Windows.Foundation.Collections;  
  8. using Windows.UI.Xaml;  
  9. using Windows.UI.Xaml.Controls;  
  10. using Windows.UI.Xaml.Controls.Primitives;  
  11. using Windows.UI.Xaml.Data;  
  12. using Windows.UI.Xaml.Input;  
  13. using Windows.UI.Xaml.Media;  
  14. using Windows.UI.Xaml.Navigation;  
  15. // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238  
  16. namespace SampleApp   
  17. {  
  18.     /// <summary>  
  19.     /// An empty page that can be used on its own or navigated to within a Frame.  
  20.     /// </summary>  
  21.     public sealed partial class MyPage: Page  
  22.     {  
  23.         public MyPage()  
  24.         {  
  25.             this.InitializeComponent();  
  26.         }  
  27.         private void btnHamburger_Click(object sender, RoutedEventArgs e)  
  28.         {  
  29.             splitView.IsPaneOpen = !splitView.IsPaneOpen;  
  30.         }  
  31.         private void appbarBtnHome_Click(object sender, RoutedEventArgs e) {}  
  32.         private void appbarBtnSettings_Click(object sender, RoutedEventArgs e) {}  
  33.     }  
  34. }  
Now try running the app on desktop and mobile --  you'll get BottomAppBar in Mobile and a Hamburger Menu in Desktop or Tablet.

Naming the Page file with extension ".DeviceFamily-Type"

This way is more similar to the previous one. Previously we've added a folder with the naming convention "DeviceFamily-Type" and put device specific UI views in there. And here instead of adding a folder for each DeviceFamily, we're going to rename the XAML View File with that same naming convention.

For example if we want to achieve the same thing as discussed inthe previous example, then first we'll add a Page with the Name "MyPage.xaml" which includes code behind as well and will act as Default View. Now we'll add one XAML View in the project folder itself and name it as "MyPage.DeviceFamily-Mobile.xaml" for Mobile UI. Also in this way the XAML View will share the code behind with the page we've added.


Now copy and paste the code we wrote in the MyPage.xaml view in previous example. You'll get the same result.

Note:


Here if you combine both ways you'll add a folder named "DeviceFamily-Mobile" and add the XAML view MyPage.xaml in that folder with that you'll also add another xaml view named "MyPage.DeviceFamily-Mobile.xaml" in the project's root folder, then the compiler will fire an exception and your app will crash. So make sure you incorporate only one of these methods.

InitializeComponent Overload Method

This way is one of the least known conceps about making DeviceSpecific UI. But believe me this is the most amazing way because it's so flexible that you can make even two UI for same page and same DeviceFamily. Let's explore this with an example.

Have you noticed as soon as you add new XAML view in DeviceFamily-Type folder or by creating new XAML view with extension of ".DeviceFamily-Type," you'll get one overloaded method of "InitializeComponent" in MyPage.g.i.cs file auto generated by Compiler.




We can use that overloaded InitializeComponent method to show different UI for same page and even for same DeviceFamily.
Check below code written in the Page's constructor to do that.
  1. bool showDefaultView = true// to see another view change it's value to 'false'  
  2. public MyPage()  
  3. {  
  4.     // In below if condition we're checking wheather the app is running on Phone or desktop  
  5.     // to do that you can also use this condtion check  
  6.     // -----if(AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Mobile")-----  
  7.     if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))  
  8.     {  
  9.         if (showDefaultView)  
  10.         {  
  11.             InitializeComponent(new Uri("ms-appx:///MyPage.xaml", UriKind.Absolute));  
  12.         } else  
  13.         {  
  14.             InitializeComponent(new Uri("ms-appx:///MyPageAnother.xaml", UriKind.Absolute));  
  15.         }  
  16.     }  
  17.     this.InitializeComponent();  
  18. }  
Here I've already added two pages as shown in this image,



Output will look like this:

Conclusion:

So there are mainly four ways to customize the UI according to the DeviceFamily,
  • using <VisualStateManager>.
  • Creating DeviceFamily specific folder with naming convention: "DeviceFamily-Type" and add XAML Views into it.
  • Creating XAML views with naming convention as "FileName.DeviceFamily-Type.xaml".
  • Using InitializeComponent Overloaded method.
I've tried to combine as many things as possible to make it a one stop solution for DeviceSpecific UI creation. If you guys have anything to add, please comment.
 
Read more articles on Universal Windows Platform:

Up Next
    Ebook Download
    View all
    Learn
    View all