How-To: Build a Responsive Layout using NavigationView and TabBar
UnoFeatures:
Navigation;Toolkit(add to<UnoFeatures>in your.csproj)
Apps that scale across multiple devices and form factors need to be able to adapt their layout to the available screen real estate. When your app is running on narrow devices, such as phones, you may want to hide the navigation pane and show a hamburger menu button instead.
It makes sense to allow TabBar to be the dominant navigation surface on these devices. On larger devices, such as tablets and desktops, you may want to show the navigation pane and hide the hamburger menu button. Using the TabBar would be a poor choice for navigation on these devices. The platform includes a VisualStateManger that allows you to define different visual states for different screen sizes in XAML, showing the navigation pane and hamburger menu button as appropriate.
This tutorial will show you how to build a responsive layout with multiple navigation controls such as NavigationView and the Uno Toolkit TabBar which use the same navigation service behind the scenes.
Step-by-step
Important
This guide assumes you used the template wizard or dotnet new unoapp to create your solution. If not, it is recommended that you follow the Creating an application with Uno.Extensions documentation to create an application from the template.
1. Add necessary XAML namespaces
Update the
Pageelement inMainPage.xamlto include XAML namespace mappings for Navigation, WinUI, and Uno Toolkit:<Page x:Class="ResponsiveShell.Views.MainPage" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" xmlns:uen="using:Uno.Extensions.Navigation.UI" xmlns:utu="using:Uno.Toolkit.UI" ...
2. Define the view's layout
Add necessary
RowDefinitionsto the rootGridinMainPage.xaml:<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <utu:NavigationBar Content="Main Page" Style="{StaticResource MaterialNavigationBarStyle}" /> </Grid>In the root
Grid, add aNavigationViewand a few simpleMenuItems:<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <utu:NavigationBar Content="Main Page" Style="{StaticResource MaterialNavigationBarStyle}" /> <muxc:NavigationView Grid.Row="1" x:Name="NavView"> <muxc:NavigationView.MenuItems> <muxc:NavigationViewItem Content="One" /> <muxc:NavigationViewItem Content="Two" /> <muxc:NavigationViewItem Content="Three" /> </muxc:NavigationView.MenuItems> </muxc:NavigationView> </Grid>Add multiple sectors of distinct content to the
NavigationViewcontent area:<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <utu:NavigationBar Content="Main Page" Style="{StaticResource MaterialNavigationBarStyle}" /> <muxc:NavigationView Grid.Row="1" x:Name="NavView"> <muxc:NavigationView.MenuItems> <muxc:NavigationViewItem Content="One" /> <muxc:NavigationViewItem Content="Two" /> <muxc:NavigationViewItem Content="Three" /> </muxc:NavigationView.MenuItems> <muxc:NavigationView.Content> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid> <Grid Visibility="Collapsed"> <TextBlock Text="One" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> <Grid Visibility="Collapsed"> <TextBlock Text="Two" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> <Grid Visibility="Collapsed"> <TextBlock Text="Three" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </Grid> </Grid> </muxc:NavigationView.Content> </muxc:NavigationView> </Grid>It's important to make each element that represents a sector of app content have it's
Visibilityexplicitly set toCollapsed. Uno.Extensions will handle toggling it back toVisiblewhen necessary.
Built for complex layout scenarios:
While the WinUI NavigationView control by itself is a good choice for a responsive shell layout because of its adaptability to different screen sizes and breakpoints, this guide will demonstrate Uno.Extensions navigation features using the TabBar control together with it.
3. Complementing the NavigationView with a TabBar
Add a
TabBarto theNavigationViewcontent area:<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <utu:NavigationBar Content="Main Page" Style="{StaticResource MaterialNavigationBarStyle}" /> <muxc:NavigationView Grid.Row="1" x:Name="NavView"> <muxc:NavigationView.MenuItems> <muxc:NavigationViewItem Content="One" /> <muxc:NavigationViewItem Content="Two" /> <muxc:NavigationViewItem Content="Three" /> </muxc:NavigationView.MenuItems> <muxc:NavigationView.Content> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid> <Grid Visibility="Collapsed"> <TextBlock Text="One" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> <Grid Visibility="Collapsed"> <TextBlock Text="Two" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> <Grid Visibility="Collapsed"> <TextBlock Text="Three" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </Grid> <utu:TabBar Grid.Row="1" x:Name="Tabs" VerticalAlignment="Bottom"> <utu:TabBar.Resources> <x:String x:Key="Icon_bolt">...</x:String> <x:String x:Key="Icon_person">...</x:String> <x:String x:Key="Icon_storefront">...<x:String> </utu:TabBar.Resources> <utu:TabBar.Items> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_storefront}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_bolt}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_storefront}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> </utu:TabBar.Items> </utu:TabBar> </Grid> </muxc:NavigationView.Content> </muxc:NavigationView> </Grid>
4. Set up regions and specify navigator type
Use the
Region.Attached="True"attached property to enable regions on the parent element for each of the following:Participating controls: The root grid of the
PageSelectable items: Both the
TabBarandNavigationViewcontrols contain selectable itemsSectors of content: Collapsed
Gridelements containing item content<Grid Region.Attached="True"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <utu:NavigationBar Content="Main Page" Style="{StaticResource MaterialNavigationBarStyle}" /> <muxc:NavigationView Grid.Row="1" Region.Attached="True" x:Name="NavView"> <muxc:NavigationView.MenuItems> <muxc:NavigationViewItem Content="One" /> <muxc:NavigationViewItem Content="Two" /> <muxc:NavigationViewItem Content="Three" /> </muxc:NavigationView.MenuItems> <muxc:NavigationView.Content> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid Region.Attached="True"> <Grid Visibility="Collapsed"> <TextBlock Text="One" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> <Grid Visibility="Collapsed"> <TextBlock Text="Two" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> <Grid Visibility="Collapsed"> <TextBlock Text="Three" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </Grid> <utu:TabBar Grid.Row="1" x:Name="Tabs" Region.Attached="True" VerticalAlignment="Bottom"> <utu:TabBar.Resources> <x:String x:Key="Icon_bolt">...</x:String> <x:String x:Key="Icon_person">...</x:String> <x:String x:Key="Icon_storefront">...<x:String> </utu:TabBar.Resources> <utu:TabBar.Items> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_storefront}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_bolt}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_storefront}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> </utu:TabBar.Items> </utu:TabBar> </Grid> </muxc:NavigationView.Content> </muxc:NavigationView> </Grid>
Name both the content itself and associated navigation control items using the
Region.Nameattached property:<Grid Region.Attached="True"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <utu:NavigationBar Content="Main Page" Style="{StaticResource MaterialNavigationBarStyle}" /> <muxc:NavigationView Grid.Row="1" Region.Attached="True" x:Name="NavView"> <muxc:NavigationView.MenuItems> <muxc:NavigationViewItem Content="One" uen:Region.Name="One" /> <muxc:NavigationViewItem Content="Two" uen:Region.Name="Two" /> <muxc:NavigationViewItem Content="Three" uen:Region.Name="Three" /> </muxc:NavigationView.MenuItems> <muxc:NavigationView.Content> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid Region.Attached="True"> <Grid Visibility="Collapsed" uen:Region.Name="One"> <TextBlock Text="One" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> <Grid Visibility="Collapsed" uen:Region.Name="Two"> <TextBlock Text="Two" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> <Grid Visibility="Collapsed" uen:Region.Name="Three"> <TextBlock Text="Three" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </Grid> <utu:TabBar Grid.Row="1" x:Name="Tabs" Region.Attached="True" VerticalAlignment="Bottom"> <utu:TabBar.Resources> <x:String x:Key="Icon_bolt">...</x:String> <x:String x:Key="Icon_person">...</x:String> <x:String x:Key="Icon_storefront">...<x:String> </utu:TabBar.Resources> <utu:TabBar.Items> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}" uen:Region.Name="One"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_storefront}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}" uen:Region.Name="Two"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_bolt}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> <utu:TabBarItem Style="{StaticResource MaterialBottomTabBarItemStyle}" uen:Region.Name="Three"> <utu:TabBarItem.Icon> <PathIcon Data="{StaticResource Icon_storefront}" /> </utu:TabBarItem.Icon> </utu:TabBarItem> </utu:TabBar.Items> </utu:TabBar> </Grid> </muxc:NavigationView.Content> </muxc:NavigationView> </Grid>Specify the navigator type as
Visibilityusing theRegion.Navigatorattached property on the parent element of your collapsed contentGriddefinitions:<Grid uen:Region.Attached="True" uen:Region.Navigator="Visibility"> ...
5. Toggle visibility for responsive design
Finally, specify groups for the
VisualStateManagerto adjust the page layout based on how close the window size is to a couple of defined breakpoints:<Page x:Class="ResponsiveShell.Views.MainPage"> <Grid uen:Region.Attached="True"> <VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualState x:Name="Narrow"> <VisualState.Setters> <Setter Target="Tabs.Visibility" Value="Visible" /> <Setter Target="NavView.IsPaneToggleButtonVisible" Value="false" /> <Setter Target="NavView.PaneDisplayMode" Value="LeftMinimal" /> <Setter Target="NavView.IsPaneOpen" Value="False" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Normal"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="700" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="Tabs.Visibility" Value="Collapsed" /> <Setter Target="NavView.IsPaneToggleButtonVisible" Value="True" /> <Setter Target="NavView.IsPaneVisible" Value="true" /> <Setter Target="NavView.PaneDisplayMode" Value="Auto" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Wide"> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="1000" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="Tabs.Visibility" Value="Collapsed" /> <Setter Target="NavView.IsPaneToggleButtonVisible" Value="True" /> <Setter Target="NavView.IsPaneVisible" Value="true" /> <Setter Target="NavView.PaneDisplayMode" Value="Auto" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> ...When a
TabBarItemorNavigationViewItemis selected, the associated content region will now have itsVisibilitytoggled toVisible