Introduction
Here it is, the second article in the series about Windows 10 new features for developers. First the index of articles:
In this article, you will see the new Adaptative VisualStates and how to adapt your page UI to various device families.
In Windows 8.1 and Windows Phone 8.1 Universal Applications, you had a project for every platform plus the shared project. This makes the process of creating an adapted user interface fairly easy. But this approach also means you end up with two app packages to publish on two stores. You simply create every page two times, one for each device family (called platform in those old days ;-) )
But, in Windows 10, you only have one project and you only generate one package. How could you adapt your user interface for every possible resolution, orientation or device type?
Mainly you have two ways. You can adapt the organization of your XAML elements using VisualStates triggered by resolution changes or you can create various XAML views for the same page.
Adaptative VisualStates
This "visual states" allows you to define a group of changes over your user interface inside your XAML, both for Pages or UserControls, efectively defining various states of your View. As an example, you could define states to modify elements visibility to show a loading control and hide or make opaque other elements when you are loading data, another state to show an element in response to a user gesture.
But the VisualStates lack an easy way to make changes based on actual window/page size. This is what the new Adaptative Visual States released with Windows 10 dev tools make. Using a new trigger, AdaptativeTrigger, allows you to create a VisualState automatically executed based on app window/page size.
Have a look at this XAML created inside MainPage.xaml in a new Blank application (UAP) project.
- <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
- <Border Margin="24,24,0,12" BorderBrush="Red" BorderThickness="2" Background="LightCyan">
- <TextBlock x:Name="TitleText" FontSize="48" Text="This is a long text title!" Foreground="Black"/>
- </Border>
- <StackPanel x:Name="ListPanel" Orientation="Vertical">
- <Image x:Name="Image01" Source="/Assets/Bilbao001.jpg" MaxHeight="120" Stretch="UniformToFill"/>
- <Image x:Name="Image02" Source="/Assets/Bilbao002.jpg" MaxHeight="120" Stretch="UniformToFill"/>
- <Image x:Name="Image03" Source="/Assets/Bilbao003.jpg" MaxHeight="120" Stretch="UniformToFill"/>
- </StackPanel>
- </StackPanel>
If you execute the app both in Windows 10 and Windows 10 mobile emulator, you should get a result like this.
The result isn't good enough in any of both platforms. In Tablet/Desktop you can see the full title, but images are too wide and don't have enough height. In the phone emulator, images are shown right, but the title is too big and you can't read it completely.
Let's create your first Visual State to adjust the title text to show correctly on the phone.
- <VisualStateManager.VisualStateGroups>
- <VisualStateGroup>
- <VisualState x:Name="NarrowState">
- <VisualState.StateTriggers>
- <AdaptiveTrigger MinWindowWidth="300"/>
- </VisualState.StateTriggers>
- <VisualState.Setters>
- <Setter Target="TitleText.FontSize"
- Value="24"/>
- </VisualState.Setters>
- </VisualState>
- </VisualStateGroup>
- </VisualStateManager.VisualStateGroups>
With the AdaptativeTrigger you can tell the system to raise the Setters when the horizontal screen resolution is larger or equal to the minimum size indicated. In this case, the Setters change the TextBlock FontSize to one more suitable for the phone.
Now, you can create another Visual State for a larger minimum resolution to adapt the content to the Tablet/Desktop.
- <VisualStateManager.VisualStateGroups>
- <VisualStateGroup>
- <VisualState x:Name="NarrowState">
- <VisualState.StateTriggers>
- <AdaptiveTrigger MinWindowWidth="300"/>
- </VisualState.StateTriggers>
- <VisualState.Setters>
- <Setter Target="TitleText.FontSize"
- Value="24"/>
- </VisualState.Setters>
- </VisualState>
- <VisualState x:Name="WideState">
- <VisualState.StateTriggers>
- <AdaptiveTrigger MinWindowWidth="600"/>
- </VisualState.StateTriggers>
- <VisualState.Setters>
- <Setter Target="ListPanel.Orientation"
- Value="Horizontal"/>
- <Setter Target="Image01.MaxHeight"
- Value="600"/>
- <Setter Target="Image02.MaxHeight"
- Value="600"/>
- <Setter Target="Image03.MaxHeight"
- Value="600"/>
- <Setter Target="Image01.MaxWidth"
- Value="300"/>
- <Setter Target="Image02.MaxWidth"
- Value="300"/>
- <Setter Target="Image03.MaxWidth"
- Value="300"/>
- </VisualState.Setters>
- </VisualState>
- </VisualStateGroup>
- </VisualStateManager.VisualStateGroups>
In this second Visual State, when the horizontal screen resolution is larger or equal to 600 pixels, the Setters change the images StackPanel orientation property to horizontal and modify max Height and Width for the images. The next image shows the result of this Adaptative triggers in action, both in a Tablet/Desktop and a Phone:
Now, this looks better. Now both the text and images are adapting their size and orientation to their container size to provide the best experience in each resolution.
It is very important to take one thing into consideration. With the Adaptative Visual States, you are working with resolutions, not with platforms. As you can see in the image above, the same layout is used in tablets and phones, based only on-screen/window resolution.
Hey, wait a moment. What if I want to create a specific View for a phone or tablet? Well, this isn't the method to do that. Today, you have smartphones like Lumia 1520 with more resolutions than some tablets. So it isn't possible to create an Adaptative Visual State to be executed only on a mobile phone.
If you want to create a View for a specific platform, have a look at the next section.
Device Family Views
Sometimes a XAML page could be very complex and attempting to fit it into the various Visual States for multiple resolutions could be a nightmare. Maybe you want to make many changes between devices' views and it is not easy to do it with the Adaptive Visual States alone.
For that situation, Microsoft has added a new concept called "Device Family Views. This allows you to have multiple XAML Views for each app Page, using conventions to identify each device correct view to render.
Let's create a new Blank application (UAP) project called Win10DeviceFamilyViews.
In MainPage.xaml, create the UI by default for Tablet/Desktop, without using the Adaptative Visual States.
- <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
- <Border Margin="24" BorderBrush="Red" BorderThickness="2"
- Background="LightCyan">
- <TextBlock x:Name="TitleText" FontSize="48"
- Text="This is the default view for mainpage"
- Foreground="Black"/>
- </Border>
- <StackPanel Margin="24" x:Name="ListPanel" Orientation="Horizontal">
- <Image x:Name="Image01" Source="/Assets/Bilbao001.jpg"
- MaxHeight="600" MaxWidth="300" Stretch="UniformToFill"/>
- <Image x:Name="Image02" Source="/Assets/Bilbao002.jpg"
- MaxHeight="600" MaxWidth="300" Stretch="UniformToFill"/>
- <Image x:Name="Image03" Source="/Assets/Bilbao003.jpg"
- MaxHeight="600" MaxWidth="300" Stretch="UniformToFill"/>
- </StackPanel>
- </StackPanel>
This result in the following UI.
Now, let's create a view specific to the device family for mobile devices.
First things first, add a new folder to the project called "DeviceFamily-Mobile". It is very important to use exactly that text for the folder since it is the name the system will look for when executing our app in the Phone device family. If a folder with that exact name exists then for each page in our app, the system would look in that folder for a view to using. If no view is located, the default is used. If a view exists, then the view XAML is used instead of the Page default XAML.
After adding the folder, add a new item to it. But don't add a new Blank Page, instead select the XAML View item to add the new item window and call it the same as your MainPage. In this case, call it MainPage.xaml:
After adding the new XAML View, you can see it doesn't have an associated .cs file like a regular page. But if you open it and hit F7 (or right-click > view code) a code-behind file is shown, but where is it located? In fact, the code-behind file you are seeing is the one from your MainPage.xaml page. Since you named the view the same as your page, they automatically share the same code-behind file. That's the reason you don't see the .cs file under the new XAML View.
Now, enter the XAML for your new View, adapted to the mobile experience. Again without Adaptative Visual States.
- <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
- <Border Margin="24,24,24,12" BorderBrush="Red" BorderThickness="2"
- Background="LightCyan">
- <TextBlock x:Name="TitleText" FontSize="26"
- Text="This is the mobile view for mainpage"
- Foreground="Black"/>
- </Border>
- <StackPanel Margin="24,0,24,24" x:Name="ListPanel" Orientation="Vertical">
- <Image x:Name="Image01" Source="/Assets/Bilbao001.jpg"
- MaxHeight="230" Stretch="UniformToFill"/>
- <Image x:Name="Image02" Source="/Assets/Bilbao002.jpg"
- MaxHeight="230" Stretch="UniformToFill"/>
- <Image x:Name="Image03" Source="/Assets/Bilbao003.jpg"
- MaxHeight="230" Stretch="UniformToFill"/>
- </StackPanel>
- </StackPanel>
The XAML, in this case, looks nearly identical to MainPage.xaml default page, but totally adapted to vertical usage and lower resolutions. You even can add new elements like a vertical scroll viewer not needed on the default page. You can do whatever you need because this is an isolated view of your page, only visible in devices from the right family.
Now if you run again the application both in your Desktop Windows 10 and on Mobile emulator, you could see in the Desktop the default XAML is rendered but in the Mobile the new View is used automatically, having the best experience in both screens:
Every device family has their independent views, giving you much flexibility, near the one you could find in universal projects for Windows/Windows Phone 8.1, but with only one project for all.
But don't think these two techniques are mutually exclusive. You can have some pages with device family defined views, some others using Adaptative Visual Stated. Even inside a Device Family View or a default page, you can use Adaptative Visual States to adapt the UI to various resolutions inside the same device family. Think of tablets: Surface Pro 1 & 2 have a resolution of 1920x1080 pixels but the Surface Pro 3 has one of 2160x1440 and some economic 7" tablets have a resolution of 1280x800. You can define a default page for tablets and a view for phones, then use the Adaptative Visual States inside every one of them to adapt content to various resolutions. This is only my humble opinion, but this is something awesome that gives you all the power to adapt to every single case you want to control.
And this was all folks
By now, there are many things to talk about and this series will continue. If you want to learn about something specific in the next articles, use the comments and let me know!
In two weeks I will fly to San Francisco to attend Build, so I hope to return with fresh news from there.
Bonus notes
Just as a curiosity, the images used in this article are from Basque Country in Spain. Precisely this is the
Guggenheim Museum in Bilbao. I lived for 4 years in a flat front of the museum and I must say, it is one of the most beautiful places I lived in.