I am writing an windows 10 UWP app and want to use the NavigationView in combination with the BackRequested
event handler to handle back navigation, however "GoBack" doesn't update the selected menu item, this means when I use the backbutton the selected menu item doesn't change. to fix this I have created an ugly foreach loop that selects the MenuItem
on back navigation using a tag. This works but I was wondering if there is a more elegant way to do this,
GoBack
doesn't fire the ItemInvoked
or SelectionChanged
event so I can't seem to be able to use those.
MainPage.xaml
<NavigationView x:Name="NavView"
CompactModeThresholdWidth="1920" ExpandedModeThresholdWidth="1920"
ItemInvoked="NavView_ItemInvoked"
SelectionChanged="NavView_SelectionChanged"
Loaded="NavView_Loaded"
Canvas.ZIndex="0">
<NavigationView.MenuItems>
<NavigationViewItem x:Uid="HomeNavItem" Content="Home" Tag="home">
<NavigationViewItem.Icon>
<FontIcon Glyph=""/>
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItemSeparator/>
</NavigationView.MenuItems>
<NavigationView.HeaderTemplate>
<DataTemplate>
<Grid Margin="24,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Style="{StaticResource TitleTextBlockStyle}"
FontSize="28"
VerticalAlignment="Center"
Text="Welcome"/>
</Grid>
</DataTemplate>
</NavigationView.HeaderTemplate>
<Frame x:Name="ContentFrame" Margin="24">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition/>
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
</NavigationView>
MainPage.xaml.cs snippet:
public MainPage()
{
this.InitializeComponent();
// initial page for ContentFrame
ContentFrame.Navigate(typeof(HomePage));
ContentFrame.Navigated += MainFrame_Navigated;
SystemNavigationManager.GetForCurrentView().BackRequested += MainPage_BackRequested;
}
private void MainPage_BackRequested(object sender, BackRequestedEventArgs e)
{
string tag = null;
if (!ContentFrame.CanGoBack) return;
e.Handled = true;
ContentFrame.GoBack();
if (ContentFrame.SourcePageType == typeof(HomePage))
{
tag = "home";
}
foreach (var navViewMenuItem in NavView.MenuItems)
{
if (navViewMenuItem is NavigationViewItem item)
{
if (item.Tag.Equals(tag)) item.IsSelected = true;
}
}
}
private void MainFrame_Navigated(object sender, NavigationEventArgs e)
{
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = ((Frame) sender).CanGoBack
? AppViewBackButtonVisibility.Visible
: AppViewBackButtonVisibility.Collapsed;
}
The NavigationView
menu items themselves could do other action than just navigation, hence the control has "no reason" to track the back navigation and update accordingly. What you can do however to set the tag of your MenuItems
and use it for both ItemInvoked
navigation and for back navigation.
NavigationView
menu items XAML would include a Tag
that exactly matches the target page type name:
<NavigationView Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<NavigationView.MenuItems>
<NavigationViewItem Content="First" Tag="FirstPage">
<NavigationViewItem.Icon>
<FontIcon Glyph="1" FontFamily="Segoe UI"/>
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItem Content="Second" Tag="SecondPage">
<NavigationViewItem.Icon>
<FontIcon Glyph="2" FontFamily="Segoe UI"/>
</NavigationViewItem.Icon>
</NavigationViewItem>
...
</NavigationView.MenuItems>
</NavigationView>
Now within the MainFrame_Navigated
method we can do the following:
//get the Type of the currently displayed page
var pageName = AppFrame.Content.GetType().Name;
//find menu item that has the matching tag
var menuItem = AppNavigationView.MenuItems
.OfType<NavigationViewItem>()
.Where(item => item.Tag.ToString() == pageName)
.First();
//select
AppNavigationView.SelectedItem = menuItem;
You can also use similar approach for ItemInvoked
handler:
var invokedMenuItem = sender.MenuItems
.OfType<NavigationViewItem>()
.Where(item =>
item.Content.ToString() ==
args.InvokedItem.ToString())
.First();
var pageTypeName = invokedMenuItem.Tag.ToString();
var pageType = Assembly.GetExecutingAssembly().GetType($"{PageNamespace}.{pageTypeName}");
AppFrame.Navigate(pageType);
Where PageNamespace
is a string constant with the name of the namespace where your Pages
are stored. You can use nameof
to keep it up to date safely, for example:
private const string PageNamespace = nameof(MyApp.Pages);
I have prepared a sample project that demonstrates these suggestions, you can check it out on my GitHub.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With