Hiding NavigationView header in UWP

Development WinUI XAML

6 years ago

The NavigationView control added in the Fall Creators Update of Windows 10 is a very useful tool for creating nice hamburger menu navigation that fits the guidelines of UWP apps. The control however includes a "header" area, that gives you a chance to provide a title of your page on the top. What if we don't want it?

The minimal problem

NavigationView has a AlwaysShowHeader property which allows you to specify if you want to display its header or not. This property works great, until the window size is too narrow and the NavigationView's display mode switches to Minimal.

Minimal mode shows the header

Minimal mode shows the header

Even when the header is empty, the content of your page is pushed down to make space for it, as though it was there. This is not very user friendly, especially on smaller screens (like phones), where every pixel matters.

Hidden in the template

If we dig into the default NavigationView's style, we can find the following XAML definition inside:

<ContentControl x:Name="HeaderContent" 
       ContentTemplate="{TemplateBinding HeaderTemplate}"
       Content="{TemplateBinding Header}" 
       HorizontalContentAlignment="Stretch" 
       IsTabStop="False"
       MinHeight="48"
       VerticalContentAlignment="Stretch"/>

The problem is quite clear - the MinHeight property is set to 48 effective pixels. In Minimal mode, the header is always shown and even when it does not contain anything, it still takes up 48 precious pixels of your screen. The solution is very simple - we can just remove the MinHeight property altogether.

The header is gone!

The header is gone!

Of course now we have one additional task - to make sure our page content count on the fact that top left corner is occluded by the hamburger button. This problem arises in the Minimal mode only, so you can quite easily use a VisualStateTrigger to add the required left margin to the top elements of your page when necessary. Following is the updated Style. You can find it in the sample source code for this article on GitHub as well.

<Style x:Key="CustomNavigationMenuStyle" TargetType="NavigationView">
    <Setter Property="PaneToggleButtonStyle" Value="{StaticResource PaneToggleButtonStyle}"/>
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="HeaderTemplate">
        <Setter.Value>
            <DataTemplate>
                <Grid Margin="12,5,0,11" VerticalAlignment="Stretch">
                    <TextBlock x:Name="Header" Style="{StaticResource TitleTextBlockStyle}" Text="{Binding}" TextWrapping="NoWrap" VerticalAlignment="Bottom"/>
                </Grid>
            </DataTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="NavigationView">
                <Grid x:Name="RootGrid">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="DisplayModeGroup">
                            <VisualState x:Name="Compact"/>
                            <VisualState x:Name="Expanded">
                                <VisualState.Setters>
                                    <Setter Target="RootSplitView.PaneBackground" Value="{ThemeResource NavigationViewExpandedPaneBackground}"/>
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="Minimal">
                                <VisualState.Setters>
                                    <Setter Target="HeaderContent.Margin" Value="48,0,0,0"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="TogglePaneGroup">
                            <VisualState x:Name="TogglePaneButtonVisible"/>
                            <VisualState x:Name="TogglePaneButtonCollapsed">
                                <VisualState.Setters>
                                    <Setter Target="TogglePaneButton.Visibility" Value="Collapsed"/>
                                    <Setter Target="PaneContentGridToggleButtonRow.Height" Value="4"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="HeaderGroup">
                            <VisualState x:Name="HeaderVisible"/>
                            <VisualState x:Name="HeaderCollapsed">
                                <VisualState.Setters>
                                    <Setter Target="HeaderContent.Visibility" Value="Collapsed"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="SettingsGroup">
                            <VisualState x:Name="SettingsVisible"/>
                            <VisualState x:Name="SettingsCollapsed">
                                <VisualState.Setters>
                                    <Setter Target="SettingsNavPaneItem.Visibility" Value="Collapsed"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="AutoSuggestGroup">
                            <VisualState x:Name="AutoSuggestBoxVisible"/>
                            <VisualState x:Name="AutoSuggestBoxCollapsed">
                                <VisualState.Setters>
                                    <Setter Target="AutoSuggestArea.Visibility" Value="Collapsed"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="PaneStateGroup">
                            <VisualState x:Name="NotClosedCompact"/>
                            <VisualState x:Name="ClosedCompact">
                                <VisualState.Setters>
                                    <Setter Target="PaneAutoSuggestBoxPresenter.Visibility" Value="Collapsed"/>
                                    <Setter Target="PaneAutoSuggestButton.Visibility" Value="Visible"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="TitleBarVisibilityGroup">
                            <VisualState x:Name="TitleBarVisible"/>
                            <VisualState x:Name="TitleBarCollapsed">
                                <VisualState.Setters>
                                    <Setter Target="PaneButtonGrid.Margin" Value="0,32,0,0"/>
                                    <Setter Target="PaneContentGrid.Margin" Value="0,32,0,0"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid HorizontalAlignment="Left" Margin="0,0,0,8" VerticalAlignment="Top" Width="{StaticResource PaneToggleButtonSize}" Canvas.ZIndex="100">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <Grid x:Name="TogglePaneTopPadding"/>
                        <Button x:Name="TogglePaneButton" AutomationProperties.LandmarkType="Navigation" Grid.Row="1" Style="{TemplateBinding PaneToggleButtonStyle}"/>
                    </Grid>
                    <SplitView x:Name="RootSplitView" Background="{TemplateBinding Background}" CompactPaneLength="{TemplateBinding CompactPaneLength}" DisplayMode="Inline" IsTabStop="False" IsPaneOpen="{Binding IsPaneOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" OpenPaneLength="{TemplateBinding OpenPaneLength}" PaneBackground="{ThemeResource NavigationViewDefaultPaneBackground}">
                        <SplitView.Pane>
                            <Grid x:Name="PaneContentGrid">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition x:Name="PaneContentGridToggleButtonRow" Height="56"/>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="8"/>
                                </Grid.RowDefinitions>
                                <Grid x:Name="ContentPaneTopPadding"/>
                                <Grid x:Name="AutoSuggestArea" Height="40" Grid.Row="2" VerticalAlignment="Center">
                                    <ContentControl x:Name="PaneAutoSuggestBoxPresenter" Content="{TemplateBinding AutoSuggestBox}" HorizontalContentAlignment="Stretch" IsTabStop="False" Margin="12,0,12,0" VerticalContentAlignment="Center"/>
                                    <Button x:Name="PaneAutoSuggestButton" Content="&#xE11A;" MinHeight="40" Style="{TemplateBinding PaneToggleButtonStyle}" Visibility="Collapsed" Width="{TemplateBinding CompactPaneLength}"/>
                                </Grid>
                                <NavigationViewList x:Name="MenuItemsHost" ItemContainerStyleSelector="{TemplateBinding MenuItemContainerStyleSelector}" ItemContainerStyle="{TemplateBinding MenuItemContainerStyle}" ItemTemplate="{TemplateBinding MenuItemTemplate}" IsItemClickEnabled="True" ItemsSource="{TemplateBinding MenuItemsSource}" ItemTemplateSelector="{TemplateBinding MenuItemTemplateSelector}" Margin="0,0,0,20" Grid.Row="3" SelectionMode="Single" SelectedItem="{TemplateBinding SelectedItem}"/>
                                <Border x:Name="FooterContentBorder" Child="{TemplateBinding PaneFooter}" Grid.Row="4"/>
                                <NavigationViewItem x:Name="SettingsNavPaneItem" Grid.Row="5">
                                    <NavigationViewItem.Icon>
                                        <SymbolIcon Symbol="Setting"/>
                                    </NavigationViewItem.Icon>
                                </NavigationViewItem>
                            </Grid>
                        </SplitView.Pane>
                        <Grid x:Name="ContentGrid">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <ContentControl x:Name="HeaderContent" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" HorizontalContentAlignment="Stretch" IsTabStop="False" VerticalContentAlignment="Stretch"/>
                            <ContentPresenter Content="{TemplateBinding Content}" Grid.Row="1"/>
                        </Grid>
                    </SplitView>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Sample code

Source code for this article is available on my GitHub.

Summary

We have shown how to make sure that NavigationView does not show any header even in the Minimal display mode and how to give our content a chance to shine on the whole area available for our app.