Windows 10 Universal Apps - Adaptive Triggers
Written by Mike James   
Thursday, 09 April 2015
Article Index
Windows 10 Universal Apps - Adaptive Triggers
Custom Triggers

With the new simpler approach to creating Universal Apps, we have to face the challenge of creating UIs that work on all devices irrespective of the screen real estate. There are a number of new facilities that make this easier than you might think.

icon

 

There are two new approaches to adjusting a XAML layout to the device - the VisualStateManager and XAML Views. Both are in the early stages of development and not well supported by Visual Studio or Blend.

The VisualStateManager has been with us for sometime, but until now it has tended to be something that was used for special occasions. With the addition of the new AdaptiveTrigger class it becomes a much more useful way to modify a layout without writing any code. In short the VisualStateManager is likely to become an essential component in almost any UI. 

Let's see how it works. 

At the time of writing neither Visual Studio nor Blend support these extensions and so we need to work with XAML directly.

AdaptiveTrigger And Setter

The VisualStateManger has a collection of VisualStateGroups each one being a set of VisualStates that may apply in a given situation. Each of the groups is independent in the sense that one VisualState from each group might be active at any one time. For simplicity, let's just deal with one VisualStateGroup from which only one VisualState is active.

In this case the basic XAML is:

<VisualStateManager.VisualStateGroups>
 <VisualStateGroup>
   list of visual states
 </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

This is how the VisualStateManager worked before Windows 10, but you could only activate a VisualState in code and the changes to the UI were specified as animations. With Windows 10 you can now activate a state using an AdaptiveTrigger object using just XAML and no code. In addition, you no longer have to pretend that your UI is changing via an animation. Now you can use a Setter to change the value of any property on any object. 

Each of the VisualState objects can have list of StateTriggers and a list of Setters and of course the order doesn't matter but it is slightly more logical to start with the StateTriggers.

The StateTriggers set conditions for the VisualState to be active and the Setters can be used to set properties on any XAML object when the VisualState is activated.

An AdaptiveTrigger takes the simple form:

<AdaptiveTrigger property=value />

the property is set to the value that you specify and this is usually used in a comparison with some other value to determine when the trigger is fires.

At the moment the only triggers are MinWindowWidth and MinWindowHeight but it is possible to add custom triggers.  These conditions test the current width and height of the window and trigger if the window is larger than the specified value.

For example:

<AdaptiveTrigger MinWindowsWidth=400 />

is active is the window' width is 400 effective pixels or greater. 

Notice that what happens is that you set the value of MinWindowsWidth to 400 and this is compared to the actual width of the window when ever it changes. The trigger fires if the actual window width is greater than or equal to MinWindowsWidth. 

A Setter takes the form:

<Setter Target="fully qualified property name"
        Value="value" />

So for example:

<Setter Target="Button1.Width"
        Value="200" />

Sets the Width of Button1 to 200 pixels if the state is active.

You can put multiple conditions and multiple setters into a single state.

For example:

<VisualState x:Name="Big">
 <VisualState.StateTriggers>
  <AdaptiveTrigger MinWindowWidth="600"/>  </VisualState.StateTriggers>
 <VisualState.Setters>
  <Setter Target="Button1.Content"
          Value="Wide"/>
 </VisualState.Setters>
</VisualState>

In this case the trigger is if the window is 600 pixels or wider. 

If the trigger fires then the Button's Content is set to "Wide" what is it set to if the trigger isn't active? 

The simple answer is that whatever value the property had before is restored when the trigger isn't active. For example, if the Button's original definition was:

<Button Name="Button1" Content="Narrow" />

the label on the Button would change to  "Wide" as the window was resized to bigger than 600 pixels and to "Narrow" when it became smaller than 599 pixels. That is, when none of the VisualStates is active, the system restores the UI to as it was origially specified in the XAML.

If you would rather have the default state included as a part of the VisualStateManager you can include it as:

<VisualState x:Name="Small">
 <VisualState.StateTriggers>
  <AdaptiveTrigger MinWindowWidth="0" />
 </VisualState.StateTriggers>
 <VisualState.Setters>
  <Setter Target="Button1.Content"
                  Value="Narrow" />
 </VisualState.Setters>
</VisualState>

Putting the whole thing together gives:

<Page
 x:Class="App4.MainPage"
 xmlns="http://schemas.microsoft.com/
                     winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/
                     winfx/2006/xaml"
 xmlns:local="using:App4"
 xmlns:d="http://schemas.microsoft.com/
                     expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/
                     markup-compatibility/2006"
 mc:Ignorable="d">
 <Grid Background="{ThemeResource
         ApplicationPageBackgroundThemeBrush}">
 
  <VisualStateManager.VisualStateGroups>
   
   <VisualStateGroup>
    <VisualState x:Name="Small">
     <VisualState.StateTriggers>
      <AdaptiveTrigger MinWindowWidth="0" />
     </VisualState.StateTriggers>
     <VisualState.Setters>
      <Setter Target="Button1.Content"
              Value="Narrow" />
     </VisualState.Setters>
    </VisualState>

    <VisualState x:Name="Big">
     <VisualState.StateTriggers>
      <AdaptiveTrigger MinWindowWidth="600" />
      </VisualState.StateTriggers>
     <VisualState.Setters>
      <Setter Target="Button1.Content"
              Value="Wide" />
     </VisualState.Setters>
    </VisualState>
   </VisualStateGroup>
  </VisualStateManager.VisualStateGroups>

 
  <Button x:Name="Button1"
          HorizontalAlignment="Left"
          VerticalAlignment="Top"
          Margin="196,172,0,0"/>
 </Grid>
</Page>

If you run this application and resize the width of the window you will see the label on the Button change as you cross the 600 pixel wide limit.

Of course, in a real application it is unlikely that you would be setting the content property of the controls. What you would be doing is setting layout properties to cope with the changing size of the window. This isn't an easy thing to do if the changes to the layout are large. It is much more suited to making small tweaks to essentially the same layout. 

icon

<ASIN:0321658701>

<ASIN:0321877586>

<ASIN:0672337266>



Last Updated ( Thursday, 09 April 2015 )