Windows 10 introduces the universal app platform for Windows apps. Windows XAML apps, specifically, enjoy several new members to their Visual Studio toolbox including the SplitView. This control is like the Grid in that it has no visible interface until something is put inside it. The purpose of the SplitView is to help developers build popular navigation experiences.
The SplitView has two content properties called Pane and Content. The Pane property is intended to be some type of navigation affordance for your Windows app. The hamburger button is an example. The Content property is intended to be a ContentPresenter for those not using the navigation framework, or the Frame for those who are (which will be most apps).
Another important SplitView property is DisplayMode. Mixed with IsPaneOpen, DisplayMode set one of four possible interaction states. I believe the most common scenario for the SplitView will be CompactOverlay, a value for DisplayMode that allows the Compact mode to be always visible and the Inline mode to overlay until it is dismissed by the user. Your Windows app may be different.
The SplitView does not invoke navigation or extend the capabilities of the navigation framework. It is a visual control. It enables XAML developers to build Windows apps with popular navigation patterns we see on the web and other mobile platforms.
The SplitView is not a design guideline. It is not part of the default template; neither is it part of a design prescription for Windows apps. The SplitView is a first-party control intended to make implementation easier for developers and designers who choose to include top-level navigation in their Windows app.
Perhaps I could offer just a little design guidance, however. The hamburger button allows for the main visual to be clean. But some developers/designers have used the hamburger “drawer” as a catch-all for everything – a disorderly junk drawer, if you will. It makes the menu unpredictable; users ask “what will I find when I click the menu button?” Bad. Remember, keep hidden menus as intuitive and easy-to-use as main visuals. That should quell much frustration.
You might have expended the SplitView to include a hamburger button. You might have expected the SplitView to include a menu button. It includes neither. The SplitView has no visual elements. It is similar to the Grid in that its final visual is the product of the developer or designer. Period.
To me, it is a real pain that the SplitView does not ship with any visual. I can respect the philosophy of not pushing me toward a certain look or feel, but most Windows apps that elect to use the SplitView probably want to use a specific look and feel common in all apps (not all, just many). This means we must build our own implementations and styles from scratch.
So I did it.
This problem is the purpose of this article. Implementing the SplitView is a little tricky. So, I thought I would share my simple approach, getting you off the ground with this nifty control.
You are going to need three files in your project (which you can steal from me):
Let’s start with the style. Remember, XAML allows developers to restyle any control simply by re-setting its Template property with a new ControlTemplate. This is most easily accomplished in Blend for Visual Studio, which is the XAML developer’s best friend for working with visuals. Because my build of Visual Studio’s designer didn’t work, I had to write it by hand. It works either way.
Let’s call our new buttons Navigation Buttons, what control should we template to create them? You’d likely start with Button, but it does not have an IsChecked property to show the current selection. You might want to wrap a Button in a ListView to get the SelectedItem propery. Forget that the ListView is not lightweight; the ListViewItem focus boundary extends to the width of the ListView which is past the width of the Compact Pane. This does not invalidate ListView; it just introduces a lot more coding.
Instead, I think RadioButton is the best choice. A RadioButton is lightweight. It also has an IsChecked dependency property that shows the current selection. The RadioButton has a square focus aboundary only around the circle, not around the RadioButton Text/Content. And, perhaps best of all, RadioButton has a GroupName property that de-selects other RadioButtons when another is selected.
When you retemplate a control in XAML there are some considerations you need to remember. First, don’t retemplate it so that keyboard navigation no longer works. Second, don’t retemplate it so that localization (varying shaped text) no longer works. Third, don’t retemplate it so that accessibility (using a screen reader, for example) no longer works. As a result, retemplating a control can be dangerous if you are making wholesale changes. For our RadioButton, we will be making small changes.
New Font Symbols
In Windows 8.x, we found glyphs in Segoe Symbol, a font that shipped with Windows. In Windows 10, we find Segoe MDL2 Assets, the font with Windows 10 glyphs. MDL2, by the way, stands for Microsoft Design Language 2.0 – put that in your trivia box for later.
Segoe MDL2 Assets has the scalable hamburger glyph you need. When it comes to icons for your buttons, please don’t use bitmaps. Fonts are a great choice because they are vector. Segoe MDL2 has tons of glyphs to choose.
Aside: would you like to learn more about the XAML RadioButton? It just so happens that I wrote an entire blog article on that control just last year. Find it here: http://blog.jerrynixon.com/2014/12/lets-code-data-binding-to-radio-button.html
It’s too bad RadioButton doesn’t have a Glyph property to set the Segoe MDL2 Assets glyph, but it doesn’t. Retemplating a control doesn’t add a new property or change its behavior. If I wanted to do that, I could subclass the control. Since we are taking the simplest approach and since we need to communicate the glyph to the template, we need to use an existing property that would otherwise be unused. That’s the Tag property.
The Tag property has been around since the dawn of time immemorial. Coincidentally, that poetic previous sentence is a near-quote from my own wedding. Stay on target. The Tag property is meant just for this type of thing. It has no programmatic purpose other than enabling an object to store some custom data. Conveniently, we can use TemplateBinding to present whatever is in our Tag property on our control. In this case, we will use Tag to hold the glyph. We don’t have a Tag2, so we have to assume we are using Segoe MDL2 Assets. If you need another font family, you will need to update the style.
MSDN: The scenario for the Tag property is to provide a general-purpose property on all FrameworkElement classes that supports data binding, animation and styles for itself but where the property's value does not have any implicit meaning to platform subsystems like layout, app model, text, input and so on.
For example, you might put a value in the Tag property that has no meaning to the FrameworkElement where it is set, but which could be useful as an ElementName binding value to some other element that uses the FrameworkElement as a DataContext and processes the Tag value in its own way.
Or you might use Tag as a way for an applied style to get a value from an arbitrary FrameworkElement parent into a specific applied template using TemplateBinding, without requiring XAML namespace mapping of a specific instance property in app XAML.
I will try to save you the time and the potential error doing that. I want you to be able to build visually consistent interfaces. That being said, please note that there is no necessity for your app should look like Microsoft first-party apps. The old, draconian days of the Microsoft design language are long past.
First, you should know that XAML now includes several new colors. Using built-in colors is a good idea because it helps to ensure your application will be compatible with changes in theme including the high contrast mode of Windows. Their actual color does not matter.
The colors relevant to us are:
- SystemListLowColor (ListHover)
- SystemListMediumColor (ListPress)
- SystemListAccentLowColor (ListSelectRest)
- SystemListAccentMediumColor (ListSelectHover)
- SystemListAccentHighColor (ListSelectPress)
It is not important that you know what these colors really are, at least not yet. They will all be predefined by the platform and injected into your app for you. If you need to tweak the colors for your own app, you will be able. But, I think you will find that changing the background of the Pane will get you 90% of the way to custom colors.
Aside: Remember you can reference resources as StaticResource or ThemeResource. Using ThemeResource instead of StaticResource means your UI can automatically update if the user has to select, for example, High Contrast colors to help with accessibility.
Second, there are Navigation Button states we will handle. These states correspond to the Control states that are native to RadioButton. This means that our style needs only to adapt the RadioButton UI so it does what a Navigation Button should do. If you are looking for a more formal term, you can call these SplitView ListItems; in other places, I have seen them called (more generically) MenuIconButtons. I don’t think either is the official name, but we have to call things something – Navigation Buttons.
A RadioButton has several possible/multiple states:
At first, it may appear to be impossible to accommodate the conflicting values of SystemListLowColor and SystemListAccentMediumColor in the PointerOver state. This is easily accomplished. Our approach is to place a rectangle behind the RadioButton, matching the desired color from the design guides. The important part to make this work is to stack their z-index appropriately.
Group and place them like this:
- Outer Container (grid)
- NotCheckedBackground (grid)
- PressedBackground (rectangle)
- HoverBackground (rectangle)
- CheckedBackground (grid)
- CheckedPressedBackground (rectangle)
- CheckedHoverBackground (rectangle)
- NotCheckedBackground (grid)
Using the CheckedStates visual state group we can reveal the CheckedBackground when the Checked visual state is active, immediately occluding the NotCheckedBackground. The PointerOver and Pressed visual states then change the visibility of both their NotChecked and Checked backgrounds. The background of the CheckedBackground (container) is set to SystemListAccentLowColor so that when no CommonStates visual state is selected (or when Normal is selected) we see the expected Accent color.
One of the things you might have noticed is that there are five possible background colors across three states. Yikes! Although any control can have more than one active visual state, there is no logic to determine, “If I am selected then use this HoverColor, else use this HoverColor.” This is a dilemma. It shows how (unfortunately) style retemplating is insufficient to accomplish exactly what we want. With enough time and money, everything is possible. Let’s solve this.
Windows 10 Preview
Not everything is working. Windows 10 is in preview. For the time being there are three lines of code in the style that are required until generic.xaml is updated. I’ll clean this up over time. But I thought you should know. There’s always the option for you to update these values to match your own app.
Why not subclass?
I didn’t subclass the SplitView or the RadioButton. But, I could have. I could have added handy dependency properties like Glyph or NavigationTarget. But I elected to this solely with Styles. Would it have worked if I had subclassed SplitView? Yes. I just chose not to.
Moving the Frame
A big assumption for the SplitView is that it contains your app’s Frame in its Content property. It took me a while to get used to this. However, since I use a Navigation Service (the Navigation Service is part of Template10) in app.xaml, where the Frame is placed becomes moot. You could put the SplitView as the root, but I elected to nest it in a Page (Shell.xaml) so I could have a designtime experience.
Your basic XAML tree will be like this:
Window.Current.Content > Shell > SplitView > Frame > MainPage
Looking closer at SplitViewStyles.xaml
This resource dictionary works like a CSS link in web development. It moves some of the more complex styling to a separate file so that it can: 1) not clutter the main page and 2) be used in more than one location, if that is relevant – which it is not in this case.
Looking closer at App.xaml
In App.xaml we create the frame, pass the frame to the shell and set the shell as the root visual. This lets met setup my NavigationService and change the root visual with very little change to the original code.
My App.xaml.cs looks like this:
In the code above, you might notice that I am using the BootStrapper from Template10. It’s because of this that our App.xaml.cs is so simplistic. Much of the boiler plate work is encapsulated for us.
The important takeaway is the OnInitializeAsync implementation. Setting the root visual (that is to say, Windows.Current.Content) to the Shell and passing the new Shell our Frame is all we need to update. The OnLaunchedAsync implementation has not changed at all.
Looking closer at Shell.xaml
In Shell.xaml we setup all our navigation buttons. Remember, all our buttons are placed in the Pane property of the SplitView. In this case, to get the look we want, we group the buttons.
There are three groups:
- Top buttons
- Back button
- Hamburger button
- Middle buttons
- All the navigation buttons
- Bottom buttons
- Settings button
Of course, the way you setup your own application may be different. Maybe you don’t want to have a Back button or the Settings button. That’s fine. I included all of them so you could have a reference to implement whatever you need in your app.
Aside: it’s possible that the number of Navigation Buttons you have could overflow the available space. For this reason, I have included a ScrollViewer. That may or may not be how you want to control overflow. In either case, it’s just something to remember.
The RadioButton XAML
The RadioButton uses the Tag property to set the glyph. It also uses its Command property to bind to the NavCommand in code-behind. The type to which the Button will navigate is defined in the RadioButton’s CommandParameter property with a strongly typed class so we get build errors if the destination views move or are deleted.
The RadioButton XAML looks like this:
In the code above, it is worth pointing out that the glyph is completely controlled in the Tag. That is to say, if the developer or designer decides their glyph needs to be an image or from another font pack, they can easily do it right there. The TextBlock styles will auto-style for standard glyphs, but because they are declared so completely, it gives you ultimate flexibility to do what you need.
Cracking open Shell.xaml.cs
The implementation of Shell.xaml code-behind is where a lot of the hard work is accomplished. The first thing you should notice is that it handles the Frame.Navigated event. This handler is present because it allows the app to navigate in ways other than the SplitView – but keeping the SplitView Navigation Buttons IsChecked property in sync with the current page.
The best part of the commanding pattern is that commands include a CanExecute property that when false renders the consuming Button as disabled. It’s awesome. In this case, we’re using Commands because they include parameters and we will need that – especially since we have already used the control’s Tag property for the glyph. Commanding will require the Command.cs file.
There are three commands in Shell.xaml.cs.
The MenuCommand is the functionality of the Hamburger button. In short, it toggles the IsPaneOpen property of the SplitView. The BackCommand is the functionality of the back button. It attempts to navigate back, if possible, and disables the back button when it is not possible.
The NavCommand is for everything else, navigating to the page specified in the RadioButton’s CommandParameter property. All frame operations are actually NavigationService methods, so they are already encapsulated and nice.
The DontCheck handler is a utility method in the Shell.xaml code-behind; we can use this for any RadioButton to ensure it is never checked. If you think about it, since the hamburger button is only a toggle of the SplitView’s IsPaneOpen property, it doesn’t make sense for it to have a selected state. The same is true with the Back button.
That’s all there is to it. Abstracting some of the implementation into separate files is only a convenience, not a requirement. If you want to roll the whole thing in a single XAML file and a single class, you could do it. But this is a simple technique.
Here are the steps to implement the SplitView:
- Copy my Mvvm/Command.cs file
- Use NuGet to add MVVM Light to your solution
- Or, update Command with your favorite framework
- Or, implement Command yourself
- Copy my Styles/SplitViewStyles.xaml
- Update the colors if you want
- Update your App.xaml.cs
- Create the frame yourself
- Pass your frame to Shell
- Make Shell your root visual
- Go ahead and navigate to main page
- Copy Shell.xaml into your project
- Hide or show the Back button
- Hide or show the Settings button
- Copy Shell.xaml.cs, too
- Update the buttons for your app
- Remember to change the glyph
- Remember to change the content
- Definitely set the correct NavType!
- Sit back and enjoy your SplitView
A final thought on implementation is around one-handed operation. If you determine that your UI should be best served with one-handed operation (which includes small tablets and most phones) you might move your hamburger button down to the bottom of the screen. There’s nothing wrong with that.
Get all the code here.
This is what it looks like. Doesn’t map to your app’s colors? Change them! Everything is set in the first few lines of the Style.
Haters are going to hate, as my daughters are fond of saying. Some designers and developers really don’t like the hamburger approach. I don’t understand the passion. But I also know that tons of designers and developers are eager to get the hamburger implemented in their Windows app, if for no other reason than to aesthetically match their apps on other platforms. Fortunately, the SplitView is here to make that easy. I hope my steps help you be successful with it.
Best of luck!