Getting started with SplitView control in Universal apps

Getting started with SplitView control in Universal apps

SplitView is a new, interesting control available in Windows 10 Universal apps and it is actually nothing more than a navigation affordance, as it's called in the Developer's Guide to Windows 10 Preview. It's also a very customizable control which doesn't really force any kind of style on you by default, except for the bare minimum of how it behaves when opened (Pane DisplayMode set to Inline, Overlay, Compact Inline, Compact Overlay), there's a default animation etc. Here's how it looks in the Xbox Music app when the pane is collapsed:

SplitView Pane is compacted

and here's the same app when pane is open:

SplitView Pane is open

(app is also trying to sign me in, but just ignore that - the pane on the left is important! :) )

You can find more details about the control in the developer's guide mentioned above, but unfortunately there's still no code sample of how to actually use the SplitView, so I tried to make the simplest possible implementation of SplitView in a demo app to see how it goes.

Note that that everything here is written using Windows 10 Technical Preview build 10041 and Technical Preview Tools, so things can (and likely will) change.

The developer's guide also mentions that the SplitView Content should be a Frame. The pane can hold whatever you add to it, but in the developer's guide they used a StackPanel of RadioButtons.

Shell as parent of SplitView

I started by creating an empty Page called Shell which holds only the SplitView

<SplitView x:Name="SplitView" Background="Black" OpenPaneLength="240" CompactPaneLength="48"  
        DisplayMode="CompactOverlay" IsPaneOpen="False" PaneBackground="DarkSeaGreen" Content="{Binding}">
    <SplitView.Pane>
        <StackPanel x:Name="SplitViewPanePanel">
            <RadioButton x:Name="BackRadioButton" Click="BackRadioButton_Click" Style="{StaticResource NavRadioButtonStyle}" Tag="&#xE112;" Background="Green" Content="Back" GroupName="Back"/>
            <RadioButton x:Name="HamburgerRadioButton" Click="HamburgerRadioButton_Click" Style="{StaticResource NavRadioButtonStyle}" Tag="&#xE700;" Content="Menu" GroupName="Hamburger"/>

            <RadioButton x:Name="HomeRadioButton" Click="HomeRadioButton_Click" Style="{StaticResource NavRadioButtonStyle}" Tag="&#xE80F;" Content="Home" GroupName="Navigation"/>
            <RadioButton x:Name="FriendsRadioButton" Click="FriendsRadioButton_Click" Style="{StaticResource NavRadioButtonStyle}" Tag="&#xE125;" Content="Friends" GroupName="Navigation"/>
        </StackPanel>
    </SplitView.Pane>
</SplitView>  

You'll notice a few very important details.

  • Pane lengths (when in open and compact mode) are set on SplitView, along with the background color and display mode.
  • Content is binding to DataContext. Later on we'll set the DataContext of the Shell to Frame, so it will show up as Content.
  • Pane holds a StackPanel with RadioButtons but it can be pretty much anything you want. RadioButtons are nice as they have checked and unchecked states.
  • RadioButtons have a Tag set to a certain character. The new character set is Segoe MDL2 Assets, and the characters used are from that set.
  • NavRadioButtonStyle is applied to RadioButtons. You will use your own styles to style the control you use.
  • Hamburger is optional! SplitView doesn't force the hamburger icon, I could've just as easily used a broccoli icon to promote healthy lifestyle (with strange consequences on the UX, I agree).
  • The position of icons is optional! SplitView doesn't force you to put icons starting from the top left corner.

New character set - Segoe MDL2 Assets

Handle events in code-behind Shell.xaml.cs

Code-behind. I know, yuck! You must be wondering where is MVVM and you're already on your way to close browser tab. But, wait, please! To keep things simple, I am not using commands - I am not writing production quality code here. KISS, let's just get started with the control! Still with me? OK :)

private void BackRadioButton_Click(object sender, RoutedEventArgs e)  
{
    var frame = this.DataContext as Frame;
    if (frame?.CanGoBack == true)
    {
        frame.GoBack();
    }
}

private void HamburgerRadioButton_Click(object sender, RoutedEventArgs e)  
{
    if (!this.SplitView.IsPaneOpen)
    {
        this.SplitView.IsPaneOpen = true;
    }
}

private void HomeRadioButton_Click(object sender, RoutedEventArgs e)  
{
    var frame = this.DataContext as Frame;
    Page page = frame?.Content as Page;
    if (page?.GetType() != typeof(HomePage))
    {
        frame.Navigate(typeof(HomePage));
    }
}

private void FriendsRadioButton_Click(object sender, RoutedEventArgs e)  
{
    var frame = this.DataContext as Frame;
    Page page = frame?.Content as Page;
    if (page?.GetType() != typeof(FriendsPage))
    {
        frame.Navigate(typeof(FriendsPage));
    }
}

HomePage and FriendsPage are simple pages that I added to Pages folder in my app. I just added some color and text to make them different (you'll see in the end).

Simple custom RadioButtonStyle

This style is oversimplified, but just helps us set the icon on the left of the Content (this is why I used Tag property - it was easier than creating a new control with a custom property just for icon).

<SolidColorBrush x:Key="NavButtonPressedBackgroundBrush" Color="White" Opacity="0.3" />  
<SolidColorBrush x:Key="NavButtonCheckedBackgroundBrush" Color="White" Opacity="0.2" />  
<SolidColorBrush x:Key="NavButtonHoverBackgroundBrush" Color="White" Opacity="0.1" />

<Style x:Key="NavRadioButtonStyle" TargetType="RadioButton">  
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Padding" Value="3"/>
    <Setter Property="HorizontalAlignment" Value="Stretch"/>
    <Setter Property="VerticalAlignment" Value="Center"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="RadioButton">
                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundGrid">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource NavButtonHoverBackgroundBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundGrid">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource NavButtonPressedBackgroundBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Disabled" />
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="CheckStates">
                            <VisualState x:Name="Checked">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BackgroundGrid">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource NavButtonCheckedBackgroundBrush}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Unchecked"/>
                            <VisualState x:Name="Indeterminate"/>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="FocusStates">
                            <VisualState x:Name="Focused"/>
                            <VisualState x:Name="Unfocused"/>
                            <VisualState x:Name="PointerFocused"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid Name="BackgroundGrid" Background="Transparent" VerticalAlignment="Stretch">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="48"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock FontSize="34" Height="38" Text="{TemplateBinding Tag}" FontFamily="Segoe MDL2 Assets" Margin="5,8,5,5" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        <ContentPresenter x:Name="ContentPresenter" AutomationProperties.AccessibilityView="Raw" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" Grid.Column="1" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" TextWrapping="Wrap" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>  

This is far from perfect, but helps to keep things simple. After all, everyone will need to think about SplitView in the context of their own app, and therefore all the styles and controls used will be different.

Initialize Shell and set the DataContext

I'm using a single Shell object in App.xaml.cs and set its DataContext to rootFrame object.

shell = Window.Current.Content as Shell;  
Frame rootFrame = null;

if (shell == null)  
{
    shell = new Shell();

    if (rootFrame == null)
    {
        rootFrame = new Frame();
        rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

        rootFrame.NavigationFailed += OnNavigationFailed;

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
        }
    }

    shell.DataContext = rootFrame;

    Window.Current.Content = shell;
}

Run the app on desktop

When you run the app, you get a very simple UI with SplitView and the famous hamburger icon.

SplitView pane compacted - Home page

SplitView pane compacted - navigated to FriendsPage

SplitView pane open - FriendsPage

(I wanted to make a GIF, but ScreenToGif, great open source tool I use, doesn't work well on Windows 10!)

Improve it a bit for Windows Phone

Now we have a great opportunity to use an adaptive trigger written by Morten Nielsen called IsTypePresentStateTrigger which allows us to do something in XAML if a certain type is present or not. We're going to check for Windows.Phone.UI.Input.HardwareButtons and hide the back button in SplitView Pane if hardware buttons exist, which is the case on Windows Phone (mobile device family)

<SplitView x:Name="SplitView" Background="Black" OpenPaneLength="240" CompactPaneLength="48"  
    DisplayMode="CompactOverlay" IsPaneOpen="False" PaneBackground="DarkSeaGreen" Content="{Binding}">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState x:Name="HardwareButtons">
                <VisualState.Setters>
                    <Setter Target="BackRadioButton.Visibility" Value="Collapsed" />
                </VisualState.Setters>
                <VisualState.StateTriggers>
                    <local:IsTypePresentStateTrigger TypeName="Windows.Phone.UI.Input.HardwareButtons" />
                </VisualState.StateTriggers>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <SplitView.Pane>
    ......
    ...
    ... the rest of the SplitView code stays the same

Mobile - SplitView pane compacted - Home page

Mobile - SplitView pane compacted - navigated to FriendsPage

Mobile - SplitView pane open - FriendsPage

Conclusion

SplitView is a cool control which gives you a lot of freedom and power to do whatever you like, and it's truly a great navigation affordance. With that comes great responsibility, so keep that in mind when designing your SplitView pane.

I hope this helps you get started with SplitView control. What do you think of SplitView? How are you using it? Leave a comment!

Igor Ralic

igor ralic

View Comments
Microsoft Certified Solutions Developer: Windows Store Apps in C#