Device family state trigger in UWP

WinUI XAML

7 years ago

Universal Windows Platform runs on many different device families. To make the app look great on different devices, we can usually use AdaptiveTriggers, which react to different screen sizes. But if we require even more specificity, we can create a device family-based state trigger.

Recognizing the device family

First we have to recognize, which device family we are currently running on. The number of device families is expected to grow, so Microsoft provides the current family as a simple string which you can retrieve using the following property:

Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamily

The value starts with the Windows. prefix, which is followed by the device family name. To make things strongly typed, we can create a simple device family helper which uses a custom enum:

/// <summary>
/// Represents different Windows 10 device families
/// </summary>
public enum DeviceFamily
{
    Unidentified,
    Desktop,
    Mobile,
    Xbox,
    Holographic,         
    IoT,
    Team,
}

/// <summary>
/// Retrieves strongly-typed device family
/// </summary>
public static class DeviceFamilyHelper
{
    static DeviceFamilyHelper()
    {
        DeviceFamily = RecognizeDeviceFamily(Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamily);
    }

    public static DeviceFamily DeviceFamily { get; }

    private static DeviceFamily RecognizeDeviceFamily(string deviceFamily)
    {
        switch (deviceFamily)
        {
            case "Windows.Mobile":
                return DeviceFamily.Mobile;
            case "Windows.Desktop":
                return DeviceFamily.Desktop;
            case "Windows.Xbox":
                return DeviceFamily.Xbox;
            case "Windows.Holographic":
                return DeviceFamily.Holographic;
            case "Windows.IoT":
                return DeviceFamily.IoT;
            case "Windows.Team":
                return DeviceFamily.Team;
            default:
                return DeviceFamily.Unidentified;
        }
    }
}

This simple helper retrieves the system property and stores the recognized device family into a read-only property. We have included a Unidentified value for device families that will be released in the future. When a new family appears, we can add new enum name and update the code accordingly.

Device family state trigger

Building custom state triggers is quite simple deriving from the StateTriggerBase and we can use the device family helper we have just created:

/// <summary>
/// Trigger to differentiate between device families
/// </summary>
public class DeviceFamilyStateTrigger : StateTriggerBase
{
    public static readonly DependencyProperty TargetDeviceFamilyProperty = DependencyProperty.Register(
        "TargetDeviceFamily", typeof(DeviceFamily), typeof(DeviceFamilyStateTrigger), new PropertyMetadata(default(DeviceFamily), OnDeviceTypePropertyChanged));

    public DeviceFamily TargetDeviceFamily
    {
        get { return (DeviceFamily)GetValue(TargetDeviceFamilyProperty); }
        set { SetValue(TargetDeviceFamilyProperty, value); }
    }

    private static void OnDeviceTypePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
    {
        var trigger = (DeviceFamilyStateTrigger)dependencyObject;
        var newTargetDeviceFamily = (DeviceFamily)eventArgs.NewValue;
        trigger.SetActive(newTargetDeviceFamily == DeviceFamilyHelper.DeviceFamily);
    }         
}

We can specify the device family to target using the TargetDeviceFamily property and then respond to the PropertyChanged event by activating or deactivating the trigger.

<Grid>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState>
                <VisualState.StateTriggers>
                    <local:DeviceFamilyStateTrigger TargetDeviceFamily="Desktop" />
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="DesktopText.Visibility" Value="Visible" />
                </VisualState.Setters>
            </VisualState>
            <VisualState>
                <VisualState.StateTriggers>
                    <local:DeviceFamilyStateTrigger TargetDeviceFamily="Mobile" />
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="MobileText.Visibility" Value="Visible" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <TextBlock x:Name="DesktopText" Text="Desktop" Visibility="Collapsed" />
    <TextBlock x:Name="MobileText" Text="Mobile" Visibility="Collapsed" />
</Grid>

This simple code snippet uses our custom trigger and shows the appropriate TextBlock . You can use the trigger to show and hide controls, change ItemTemplates for lists and more.

Source code

The source code for this post is available on my GitHub.

Summary

When we provide device-family specific functionality to the users, we usually have to update the displayed view appropriately. Our device family state trigger will help us do just that. If you need to specialize your views even more, you can create custom device family views. We will show this technique in a future post.