Building a Hamburger Menu for your Universal App

One of the first thing to do when building a Universal Windows Platform (UWP) Application is to choose how the application will present its features to the user. The Hamburger Menu is one of the most popular designs for implementing navigation in a universal app. Its use is pervasive across all Microsoft apps and a lot of third-party apps as well. Evidently, the SafetyBox App will also feature navigation based upon a Hamburger Menu.

I have found a lot of information about building such a feature in my app. Specifically, the following articles or samples were used as inspiration for this article:

However, in this article, I will try to present a consolidated approach that makes sense and present the concepts in the order I stumbled upon when trying to implement this menu in my own app.

Creating the “Chrome” of the App in a Shell.xaml Page

In order to power a Hamburger-based navigation menu, one has to use the SplitView control, designed to separate the contents of a page into two parts:

  • The left part, called the navigation Pane, consisting in the hamburger menu itself.
  • The right part, designed to host a Frame, consisting in any page that the user wants to navigate to.

The navigation pane can be invisible, or displayed in Compact or Open mode. The Compact mode typically displays only the hamburger button itself and the navigation options in the form of icons. The Open mode diplays additional text for the navigation options, as well as a richer set of interactive options if the application requires it.

A default project for building universal applications create an App.xaml application object as well as a MainPage.xaml page. One of the first task when implementing the Hamburger menu is to change the MainPage.xaml to act as a Shell that provides navigations to other pages.

There are several options for laying out the Hamburger menu.

One popular option is the one used by most builtin MSN/Live Apps, such as News, Finances, Weather, etc. This is done with the following layout, where you can notice that the Hamburger button is actually not part of the SplitView pane.

A snapshot of the News MSN App where the hamburger button is not part of the SplitView pane.

Another option is to include the Hamburger button inside the SplitView pane. This approach is used by the Groove App, for instance.

A snapshot of the Groove Music App where the hamburger button is part of the SplitView pane.

In this article, I will present a solution for implementing the latter option, similar to the Groove Music App.

Another choice to make is how to implement the SplitView pane menu items.

I have personally chosen to use RadioButton controls for several reasons. First, conceptually, the options in a SplitView pane can be thought of as a group of mutually exclusive options. Second, it is very easy to apply custom styling to a RadioButton via a redefined control template. Since styling is easy, it is also easy to include support for changing the colors on mouse/pointer hover.

Another option would have been to use a ListView control with single selection enabled, for instance. A ListView is a bit more cumbersome to use, however, because have to use a custom class that represent ListView items and use a DataTemplate to apply styling and custom behavior. Also, one has to include special code to support navigating the list with the keyboard without triggering item selection.

With that said, let’s dive into the code!

Starting from a blank project for a universal application, make the following changes:

  1. First, you may optionally want to rename MainPage.xaml to Shell.xaml.
  2. Open MainPage.xaml (or Shell.xaml), and replace the empty Grid control with the following code:
<SplitView x:Name="NavigationPane" DisplayMode="CompactInline">
  <SplitView.Pane>

    <Grid>

      <Grid.RowDefinitions>
        <RowDefinition Height="44" />
        <RowDefinition Height="*"/>
      </Grid.RowDefinitions>

      <Button x:Name="HamburgerButton" Grid.Row="0" Style="{StaticResource MenuItemButtonStyle}" Tag="& #xE700;" Click="HamburgerButton_Click" />

      <StackPanel x:Name="NavigationMenu" Orientation="Vertical" Grid.Row="1">
        <RadioButton x:Name="Option1"
           GroupName="Group1"
           Style="{StaticResource NavigationButtonStyle}"
           Tag="& #xE76E;"
           Checked="Option1Button_Checked"
           Content="Option 1"
           />
        <RadioButton x:Name="Option2"
           GroupName="Group1"
           Style="{StaticResource NavigationButtonStyle}"
           Tag="& #xE76E;"
           Checked="Option2Button_Checked"
           Content="Option 2"
           />
      </StackPanel>

    </Grid>

  </SplitView.Pane>
  <SplitView.Content>

    <Frame x:Name="Content">
      <Frame.ContentTransitions>
        <TransitionCollection>
          <NavigationThemeTransition>
            <NavigationThemeTransition.DefaultNavigationTransitionInfo>
              <EntranceNavigationTransitionInfo/>
            </NavigationThemeTransition.DefaultNavigationTransitionInfo>
          </NavigationThemeTransition>
        </TransitionCollection>
      </Frame.ContentTransitions>
    </Frame>
  </SplitView.Content>
</SplitView>

This code uses the otherwise unused Tag property of XAML controls to store the icon associated with a particular option. This icon is designated with a code that maps to the character code using the Windows 10 font named Segoe MDL Assets 2.

  1. Open MainPage.xaml.cs (or Shell.xaml.cs) and paste the following code:
public Frame AppFrame { get { return Content; } }

private void Option1Button_Checked(object sender, RoutedEventArgs e)
{

}

private void Option2Button_Checked(object sender, RoutedEventArgs e)
{

}

private void HamburgerButton_Click(object sender, RoutedEventArgs e)
{
    NavigationPane.IsPaneOpen = !NavigationPane.IsPaneOpen;
}

The layout code makes use of static resources to style the hamburger button as well as the radio buttons that act as navigation options:

  1. Open App.xaml and past the following code to enable access to additional resources:
<Application.Resources>
  <ResourceDictionary>

    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="Themes/Generic.xaml"/>
    </ResourceDictionary.MergedDictionaries>

  </ResourceDictionary>
</Application.Resources>


  1. Create a file named Themes\Generic.xaml to host the resources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


<Style TargetType="Button" x:Name="MenuItemButtonStyle" x:Key="MenuItemButtonStyle">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}"/>
    <Setter Property="MinWidth" Value="{ThemeResource SplitViewCompactPaneThemeLength}" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Button">

          <Grid x:Name="RootGrid">

            <Grid.RowDefinitions>
              <RowDefinition Height="44" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="48" />
              <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
        
            <!-- use extra left margin to align the icon with NavigationButtonStyle'd controls -->
            <FontIcon Grid.Column="0"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                Margin="12,8,8,8"
                FontFamily="{ThemeResource SymbolThemeFontFamily}"
                Glyph="{TemplateBinding Tag}"
                />
            <TextBlock Grid.Column="1"
                VerticalAlignment="Center"
                HorizontalAlignment="Left"
                Margin="8,8,8,8"
                Text="{TemplateBinding Content}"
                />
          </Grid>

        </ControlTemplate>
      </Setter.Value>
    </Setter>

  </Style>

<Style TargetType="RadioButton" x:Name="NavigationButtonStyle" x:Key="NavigationButtonStyle">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}"/>
    <Setter Property="MinWidth" Value="{ThemeResource SplitViewCompactPaneThemeLength}" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="RadioButton">

          <Grid x:Name="RootGrid">

            <VisualStateManager.VisualStateGroups>
              <VisualStateGroup x:Name="CheckedStates">
                <VisualState x:Name="Checked">
                  <Storyboard>
                    <ColorAnimation Storyboard.TargetName="Brush" Storyboard.TargetProperty="Color" From="{Binding Path=Background}" To="Blue" Duration="0:0:0" FillBehavior="HoldEnd" />
                  </Storyboard>
                </VisualState>
                <VisualState x:Name="Unchecked" />
                <VisualState x:Name="Indeterminate" />
              </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>

            <Grid.RowDefinitions>
              <RowDefinition Height="44" />
            </Grid.RowDefinitions>

            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="4" />
              <ColumnDefinition Width="44" />
              <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Rectangle Grid.Column="0">
              <Rectangle.Fill>
                <SolidColorBrush x:Name="Brush" Color="{Binding Path=Background}" />
              </Rectangle.Fill>
            </Rectangle>

            <FontIcon Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="8,8,8,8" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="{TemplateBinding Tag}" />
            <TextBlock Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="8,8,8,8" Text="{TemplateBinding Content}" />

          </Grid>

        </ControlTemplate>
      </Setter.Value>
    </Setter>

  </Style>

</ResourceDictionary>

Two styles are defined in the Generic.xaml file.

The second style, named NavigationButtonStyle, is targeting RadioButton controls in the Hamburger menu. This style defines a layout with three columns. From left to right, one finds a) a selection-rectangle that highlights when the particular option is selected, b) an icon and, c) a text, displayed when the SplitView pane is open. This style includes visual triggers using a ColorAnimation to highlight the selection rectangle in blue when the particular option is selected.

The first style, named MenuItemButtonStyle, is targeting regular Button controls. In particular, this is the style used by the Hamburger button itself. You will notice that is uses a two-columns layout, with an icon and a text. Notice that the icon’s left margin is adjusted so as to be aligned with icons displayed by the first style.

At this stage, you should have a basic working application:

A basic universal app with a popular Hamburger navigation menu

In the next post, I will walk you through adding proper mouse/pointer hover feedback and handling navigation using the keyboard.

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