vendredi 25 mars 2016

Using adaptive triggers to build an application that can run on multiple Windows 10 platforms (Mobile/Desktop) XAML

Building applications is hard, and making them compatible on multiple platforms is a headache. You can either create multiple apps for specific platforms and use TargetDeviceFamily like in this tutorial or use AdaptiveTriggers so that you can adapt your view to the screen of a device.
On the Dailymotion Windows 10 application we wanted to use the same views on Mobile and Desktop devices. This is where adaptive triggers come to the rescue.
In this tutorial I will explain how to use AdaptiveTriggers to resize the width of our ItemTemplate inside a Gridview, when the application changes size. When the application is reduced to a certain size, I want the ItemTemplate that has a fixed size (300 pixels) to take up the whole width of the page.
Because a Gif is better then a 1000 words, this is the effect that I would like:
Adaptive cats

This tutorial will cover:

  1. What are AdaptiveTriggers
  2. How to use AdaptiveTriggers on a DataTemplate

What are Adaptive Triggers

Quick description: adaptive triggers allow you to create a rule (or set of rules) that can trigger a change in the visual state of your application (you can change the Size/Color/Visibility/etc of an element for example). This can allow you to resize elements without having to listen to the Window.SizeChanged event (that's AWESOME! let's do this!).
A lot more information can be found on MSDN here

How to use adaptive triggers on Items inside a GridView

The trick to using adaptive triggers inside a GridView is to implement it inside theDataTemplate, however, you can't create a
 <VisualStateManager.VisualStateGroups />
inside a resource file, so how is this possible?
You need to create a UserControl that you reference in your DataTemplate like this:
 <DataTemplate>
     <controls:AdaptiveCatItem />
 </DataTemplate>
And you set your VisualStateManager in the UserControl.
Here is an example on my User Control:
<UserControl x:Class="AdaptiveTriggers.Win10.Controls.AdaptiveCatItem"             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
    <Grid x:Name="TemplateRoot"
          Height="250"
          Width="300"
          HorizontalAlignment="Stretch"
          Margin="2"
          VerticalAlignment="Top">        
          <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="VisualStateGroup">
                <VisualState x:Name="VisualStateNarrow">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger x:Name="VisualStateNarrowTrigger" MinWindowWidth="0" />                              
          </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="TemplateRoot.Width" Value="Auto" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="VisualStateNormal">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger x:Name="VisualStateNormalTrigger" MinWindowWidth="521" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="TemplateRoot.Width" Value="300" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <Grid.RowDefinitions>
            <RowDefinition Height="190" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>   

        <Image Grid.Row="0"
               HorizontalAlignment="Center"
               Source="{Binding ThumbnailUri}"
               Stretch="UniformToFill" />

        <Grid Grid.Row="1"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Top"
              Background="Transparent">
            <TextBlock Grid.Row="0"
                       Margin="5,5,5,5"
                       Text="{Binding Title,
                       FallbackValue='This is the cat image of the year'}"
                       />
        </Grid>
    </Grid>    
</UserControl>  
My VisualStateManager has two important AdaptiveTriggers. The first one is named VisualStateNarrowTrigger with a MinWindowWidth of 0 and another one named VisualStateNormalTrigger with a MinWindowWidth of 521. In this example, VisualStateNormalTrigger is the most important one.
What VisualStateNormalTrigger does is when the application is larger than 521 pixels then my items in the GridView have a width of 300. When my application width is smaller than 521 pixels the trigger VisualStateNarrowTrigger is fired and the width of my items in the GridView are set to Auto (which means that it will take up as much space as it can).
Going back to my VisualStateManager, here are the two important parts:
  • As mentioned above, VisualState allows me to set the width to auto on my grid element named TemplateRoot, when the application has a width of 0 pixels or more:
 <VisualState.StateTriggers>
     <AdaptiveTrigger x:Name="VisualStateNarrowTrigger" MinWindowWidth="0" />
 </VisualState.StateTriggers>
 <VisualState.Setters>
    <Setter Target="TemplateRoot.Width" Value="Auto" />
 </VisualState.Setters>
  • This VisualState allows me to set the Width to 300 on my grid element named TemplateRoot when the width of my application is 521 pixel or more:
   <VisualState.StateTriggers>
      <AdaptiveTrigger x:Name="VisualStateNormalTrigger" MinWindowWidth="521" />
   </VisualState.StateTriggers>
   <VisualState.Setters>
       <Setter Target="TemplateRoot.Width" Value="300" />
   </VisualState.Setters>
And there you have it, you can now resize the items that are inside your GridView when your application is resized without using any C# codeAdaptive cats
All of the code can be found on Github here
This piece of XAML code is what has allowed our Dailymotion application to be both Mobile and Tablet compatible without havving to code two appsDownload our sample here and give it a try!
Happy Coding =)

PS: as you can guess I like cats ^^