5 Tips for Designing Beautiful UWP Apps

Datetime:2016-08-22 22:15:28          Topic:          Share

This blog post is about the design ideas and tricks that went into building the UWP version of the Property Manager code sample (recently published at https://github.com/microsoftgraph/xamarin-csharp-propertymanager-sample ). The code sample is a cross-platform solution, leveraging the Microsoft Graph and focuses on building native user experiences. If you would like to learn more about the code sample itself, visit this blog post:  http://simonjaeger.com/a-xamarin-native-sample-for-the-microsoft-graph/

You will find a lot of resemblance with other applications (such as the settings app and messaging app) in Windows 10 for the code sample – and that’s exactly what we’re trying to achieve. The blog post is divided into 5 topics that contribute to creating a user experiences that feels like it belongs within Windows 10.

1. Everyone loves animations

Some of the iconic features of a platform is its animations and transitions. It goes for practically any operating system, and luckily – it’s super simple to use animations within your UWP apps. It may be a simple thing to achieve code-wise, but the effect on the user experience is drastic. Adding this, you will find users experiencing your application to be more polished and enjoyable to interact with.

In its most simplest form, you can add a NavigationThemeTransition to your XAML pages. By configuring a NavigationThemeTransition, the system will take care of animating your content between page navigation. This way, you don’t have to worry about applying any properties or modifications to your existing markup.

<views:MvxWindowsPage.Transitions>
    <TransitionCollection>
        <NavigationThemeTransition>
            <NavigationThemeTransition.DefaultNavigationTransitionInfo>
                <ContinuumNavigationTransitionInfo/>
            </NavigationThemeTransition.DefaultNavigationTransitionInfo>
        </NavigationThemeTransition>
    </TransitionCollection>
</views:MvxWindowsPage.Transitions>

Now, for the system to know how the animation should behave you will need to supply it with some transition information. The code sample uses a built-in ContinuumNavigationTransitionInfo object, which contains the transition information for the navigation animations that many of us are used to see in Windows 10.

2. Play with typography – but be consistent

The use of typography in a sophisticated manner is a great way of drawing attention to the places you want. In Windows 10 apps, you might be used to the Segoe font family. You will most likely want to stay with these fonts, but there are a few different tricks you can apply to divert attention and flow to the right places.

  • Font weights: work with both lighter and bolder formats.
  • Font sizes: headers and titles are usually highlighted with larger font sizes – while bodies and paragraphs would be drawn with smaller variants.
  • Colors (RGB and A): Use an accent color where it may be needed to highlight something out of the ordinary. You can also work with the opacity to lower the significance of a particular piece of content.

Your key is to bring attention to areas of higher importance than other areas. Some content may be interesting for the user, while some may be absolutely crucial for navigation and actions. For example, less important content may be toned down by using any of the above approaches.

Remember to be consistent. Settle for a pattern when it comes to establishing your font styles. A great way of doing this is by organizing your styles statically and reusing them across your XAML pages. Such as in the Styles.xaml file, which is a resource dictionary containing all of the reusable styles in the code sample:

<Stylex:Key="FadedBodyTextBlock" TargetType="TextBlock">
    <SetterProperty="Opacity" Value="0.6"/>
    <SetterProperty="FontSize" Value="14"/>
</Style>
 
<Stylex:Key="HeaderTextBlock" TargetType="TextBlock">
    <SetterProperty="FontSize" Value="24"/>
    <SetterProperty="FontWeight" Value="SemiLight"/>
</Style>

3. Be responsive, you must

The wonderful thing about building anything for the UWP universe is the fact that you’ll be able to run your hard work on a wide range of devices flavors. In short, your app will run on everything for an IoT device, a phone, a desktop PC, a Surface Hub, a HoloLens and so on… This means that you will need to make your app play nice with different screen sizes and form factors. Fortunately, you have a lot of tools at your disposal to deal with this challenge.

One of the things to think about is if your content should need to be scrollable. Ask yourself; if you resize your app into various sizes, can you still reach all of the content? If not, you can leverage the ScrollViewer control. It lets you wrap your content into a scrollable area. You can easily control which direction(s) the content should be scrolled in by using the mode and visibility properties ( VerticalScrollBarVisibility and  VerticalScrollMode) .

Using a ScrollViewer with a child StackPanel control (which stacks its own children infinitely in one given orientation) creates a great combination for control flow.

<ScrollViewerGrid.Row="1" Padding="15" VerticalScrollBarVisibility="Auto"
                VerticalScrollMode="Auto">
    <StackPanelOrientation="Vertical" HorizontalAlignment="Stretch">
        <!--Your Content Goes Here--> 
    </StackPanel>
</ScrollViewer>

Now scrolling might not always be the best solution. Sometimes it’s better to be responsive, that could however mean a few different things. You can either instruct the controls to operate differently, or simply swap them out for a more appropriate control. Another option is just to extend the amount of content on the screen (but do not overwhelm the user!).

In the code sample, we leverage a built-in feature called AdaptiveTrigger . If you are familiar with CSS media queries, you’ll know the idea of the AdaptiveTrigger. In short, this allows you to declare a rule which applies visual states in your markup. Basically, you can declare a set of controls to be visible, if the window size is below a certain value. Using this concept, we can create responsive experiences – all in markup!

Consider the below markup, defined in the GroupsView.xaml file. If you look at the AdaptiveTrigger elements, you’ll see for which condition (in MinWindowWith) the Setters in the parent VisualState element will be applied to the XAML page.

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup>
        <VisualStatex:Name="Normal">
            <VisualState.Setters>
                <SetterTarget="SearchBox.HorizontalAlignment" Value="Center"></Setter>
                <SetterTarget="SearchBox.Width" Value="340"></Setter>
                <SetterTarget="SearchBox.Margin" Value="0,75,0,0"></Setter>
                <SetterTarget="SlimGridView.Visibility" Value="Collapsed"></Setter>
                <SetterTarget="NormalGridView.Visibility" Value="Visible"></Setter>
                <SetterTarget="TitleTextBlock.Visibility" Value="Visible"></Setter>
                <SetterTarget="AddPropertyButton.Visibility" Value="Visible"></Setter>
            </VisualState.Setters>
            <VisualState.StateTriggers>
                <AdaptiveTriggerMinWindowWidth="700"></AdaptiveTrigger>
            </VisualState.StateTriggers>
        </VisualState>
        <VisualStatex:Name="Slim">
            <VisualState.Setters>
                <SetterTarget="SearchBox.HorizontalAlignment" Value="Stretch"></Setter>
                <SetterTarget="SlimGridView.Visibility" Value="Visible"></Setter>
                <SetterTarget="NormalGridView.Visibility" Value="Collapsed"></Setter>
                <SetterTarget="TitleTextBlock.Visibility" Value="Collapsed"></Setter>
                <SetterTarget="AddPropertyButton.Visibility" Value="Collapsed"></Setter>
            </VisualState.Setters>
            <VisualState.StateTriggers>
                <AdaptiveTriggerMinWindowWidth="0"></AdaptiveTrigger>
            </VisualState.StateTriggers>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

4. Create custom UserControl’s for recurring compositions

If you find yourself “copy-pasting” pieces from one XAML page to another – you might be in a good place to create a custom UserControl . Not only does this allow you to save time in the long run, but it also makes sure your experiences are consistent across your solution.

In the code sample, a custom TextBlock named PointerTextBlock  is used. It highlights a label when the cursor hovers over it and swaps the cursor into the hand pointer. This requires some code to be wired up. Reusing both the XAML and code-behind in a UserControl creates a healthy approach. While the TextBlock is contained within the UserControl, its Text property is exposed via a DependencyProperty .

The UserControl is implemented as such:

public sealed partialclass PointerTextBlock : UserControl
{
    private static readonly CoreCursorHandCursor = new CoreCursor(CoreCursorType.Hand, 1);
    private static readonly CoreCursorArrowCursor = new CoreCursor(CoreCursorType.Arrow, 1);
 
    public static readonly DependencyPropertyTextProperty = DependencyProperty.Register(
        "Text", typeof (string), typeof (PointerTextBlock), new PropertyMetadata(default(string)));
 
    public string Text
    {
        get { return (string) GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
 
    public PointerTextBlock()
    {
        InitializeComponent();
    }
 
    private void OnPointerEntered(object sender, PointerRoutedEventArgs e)
    {
        Window.Current.CoreWindow.PointerCursor = HandCursor;
        Opacity = 0.7;
    }
 
    private void OnPointerExited(object sender, PointerRoutedEventArgs e)
    {
        Window.Current.CoreWindow.PointerCursor = ArrowCursor;
        Opacity = 1;
    }
}

Later on, it’s easily reused across the XAML pages:

<controls:PointerTextBlockText="Edit details" Foreground="{StaticResource AccentColorBrush}"
                          Margin="0,15,0,0" Tapped="OnEditDetailsButtonTapped"/>

5. If variation is needed – DataTemplateSelector’s may help

If you would like to show different types of items in a list control (different templates), you can implement what’s called a DataTemplateSelector . What it does is pretty straight forward; it lets you write a custom piece of logic to choose which template the parent control should use for every list item. The code sample implements one of these ( ConversationMessageTemplateSelector ) in order to select differently aligned message boxes (left for messages from others, right for messages that the user sent) for the conversation list. It is implemented as such:

public class ConversationMessageTemplateSelector : DataTemplateSelector
{
    protected override DataTemplateSelectTemplateCore(object item, DependencyObjectcontainer)
    {
        var conversation = itemas ConversationModel;
        if (conversation == null)
        {
            return base.SelectTemplateCore(item, container);
        }
            
        var resources = Application.Current.Resources;
        if (conversation.IsOwnedByUser)
        {
            return resources["ConversationMessageRightTemplate"] as DataTemplate;
        }
        return resources["ConversationMessageLeftTemplate"] as DataTemplate;
    }
}

It is then placed in the Styles.xaml file, in order to make it accessable across the XAML pages.

<controls:ConversationMessageTemplateSelectorx:Key="ConversationMessageTemplateSelector"/>

The list control ( GridView ) finally references the DataTemplateSelector as its ItemTemplateSelector, and the magic is wired up.

<GridViewx:Name="ConversationsGridView" Grid.Row="0" 
            ItemsSource="{Binding Conversations}"
            Style="{StaticResource GridViewStyle}"
            ItemTemplateSelector="{StaticResource ConversationMessageTemplateSelector}">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanelOrientation="Vertical"/>
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
</GridView>

Sharing is caring – contribute!

If you find yourself with a bug fix or an improvement – please do share it! If you can’t implement it on your own, create an issue within the GitHub repository and it will be looked at. If you can, go ahead and create your implementation and submit a pull request. It will only make the code sample even better! Thanks!

https://github.com/microsoftgraph/xamarin-csharp-propertymanager-sample

-Simon Jaeger