Taking advantage of Continuum with Adaptive UI

With Windows 10, Microsoft introduces Continuum, a feature that allows one to use the phone as the central hub of a PC-like experience with a big screen, a keyboard and, optionally, a mouse.

A Continuum-enabled UWP App should provide a seamless experience when used in both environments. For instance, all the built in applications that ship with Windows 10 are Continuum-enabled Universal Window Platform Apps. Mail, News, Finance, Weather, etc. These are all applications that are usable from the touch-based Phone screen and lighten up with more features and more controls when used from an external display with a mouse and keyboard.

In order to achieve that, a developer can use several new controls that ship with Windows 10. For instance, the SplitView control provides the popular Hamburger-based navigation menu. Also, the RelativePanel is a container that helps create complex layout that can be modified to accommodate more screen real estate by specifying spatial-contraints on its child controls.

Additionally, a developer can take advantage of Adaptive Coding which refers to a set of techniques that help create a single application targeting multiple devices, from small phones to tablets to desktops and more.

There are two sides to Adaptive Coding:

  1. The first one deals with providing the best screen layout and navigation based upon the screen real estate, the current device orientation and the available features.
  2. The second one deals with using features specific to a particular device by detecting whether a particular type or API Contract is present.

In this post, I will highlight how to tackle the first part, thanks to the VisualStateManager class.

The Visual State Manager in Windows 10 takes advantage of Visual State Triggers which are a set of conditions on various states of the application to modify the appearance and organization of one or more controls.

For instance, most examples on this subject show how one can change the color of the background depending upon the current window Width. Several other examples show how one can take advantage of the device orientation to achieve similar results.

In my example, I will show how one can create a custom StateTrigger, i.e. a class that derives from the StateTriggerBase class to detect the current UserInteractionMode. The User Interaction Mode is a value that specifies whether the app is currently being used by using Touch gestures or by using a Mouse and keyboard.

When an App transitions from the Phone screen to an external display thanks to Continuum, the UserInteractionMode changes from Touch to Mouse. That is one of many ways that a developer can take advantage of this feature.

Creating a custom UserInteractionMode state trigger

The idea behind this example is to create a custom class that can be used as a Visual State Trigger in the XAML page. The condition being whether the particular user interaction mode is the one associated with a phone screen – i.e. touch – or the one associated with an external display monitor – i.e. mouse.

This allows to build apps that look like this:

Basic Text Editor App when used on the Phone

Basic Text Editor App when used on the Phone

In your application, create a new file, named UserInteractionModeTrigger and paste the following code:

using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;

public class UserInteractionModeTrigger : StateTriggerBase
{
    ///

<summary>
    /// Initialize a new instance of the <see cref="UserInteractionModeTrigger" /> class.
    /// </summary>


    public UserInteractionModeTrigger()
    {
        Initialize();
    }

    ///

<summary>
    /// Get or set the current UserInteractionMode, i.e. Touch or Mouse.
    /// </summary>


    public string CurrentUserInteractionMode
    {
        get { return (string)GetValue(UIModeProperty); }
        set { SetValue(UIModeProperty, value); }
    }

    public static readonly DependencyProperty UIModeProperty =
        DependencyProperty.Register("UIMode"
            , typeof(string)
            , typeof(UserInteractionModeTrigger),
            new PropertyMetadata("")
            );

    private void Initialize()
    {
        if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled)
        {
            WindowActivatedEventHandler windowactivated = null;
            windowactivated = (s, e) =>
            {
                Window.Current.Activated -= windowactivated;
                Current_SizeChanged(this, null);
            };

            Window.Current.Activated += windowactivated;
            Window.Current.SizeChanged += Current_SizeChanged;
        }
    }

    private void Current_SizeChanged(object sender, WindowSizeChangedEventArgs e)
    {
        var currentUIMode = UIViewSettings.GetForCurrentView().UserInteractionMode.ToString();
        SetActive(currentUIMode == CurrentUserInteractionMode);
    }
}

This is a simple custom state trigger. Notice that when Initialized, the trigger registers itself on the Activated and SizeChanged events in order to raise a trigger the first time the application is launched. Otherwise, the trigger would only happen during a transition from the Phone screen to the external display.

You can use this call in your app, for instance, like so:

<Page ...
      xmlns:trigg="using:App.Triggers"
...
<Grid>
 
    <!-- The VisualState Groups must be enclosed -- in the first top-level container on a Page. -->

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="UserInteractionModeStates">
            <VisualStateGroup.States>
                <VisualState x:Name="MouseState">
                    <VisualState.StateTriggers>
                        <trigg:UserInteractionModeTrigger CurrentUserInteractionMode="Mouse" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BottomCommandBar.Visibility" Value="Collapsed" />
                        <Setter Target="Header.Visibility" Value="Collapsed" />
                        <Setter Target="SplitView.(Grid.Row)" Value="0" />
                        <Setter Target="SplitView.(Grid.RowSpan)" Value="2" />
                        <Setter Target="TopCommandBar.Visibility" Value="Visible" />
                    </VisualState.Setters>
                </VisualState>

                <VisualState x:Name="NarrowState">
                    <VisualState.StateTriggers>
                        <trigg:UserInteractionModeTrigger CurrentUserInteractionMode="Touch" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BottomCommandBar.Visibility" Value="Visible" />
                        <Setter Target="Header.Visibility" Value="Visible" />
                        <Setter Target="SplitView.(Grid.Row)" Value="1" />
                        <Setter Target="SplitView.(Grid.RowSpan)" Value="1" />
                        <Setter Target="TopCommandBar.Visibility" Value="Collapsed" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup.States>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    
</Grid>

The following article has been used as a reference for this post:

This entry was posted in UWP and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s