Navigation Issue in UWP

2019-09-10 07:54发布

问题:

tl;dr

I have 3 Pages MainPage.xaml, BlankPage1.xaml, BlankPage2.xaml with Button on MainPage and BlankPage1 that navigates to BlankPage1 and BlankPage2 Respectively. I enabled System Back Button so that i can go back to previous page.

Button Tap on MainPage navigates to BlankPage1 and Button Tap on BlankPage1 navigates to BlankPage2. This works fine.

Problem: When I tap Back Button on BlankPage2 it goes back to BlankPage. Now when I tap the Button on BlankPage1 it goes to BlankPage2 but when i Tap the Back Button, instead of going to BlankPage1 it navigates directly to MainPage.

Below is my code.

MainPage.xaml

<Page
    x:Class="App2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Button Content="GoTo Page 1" HorizontalAlignment="Center" VerticalAlignment="Center" Tapped="Button_Tapped"/>
    </Grid>
</Page>

MainPage.xaml.cs

using Windows.UI.Xaml.Controls;

namespace App2
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private void Button_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
        {
            Frame.Navigate(typeof(BlankPage1));
        }
    }
}

BlankPage1.xaml

<Page
    x:Class="App2.BlankPage1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Button Content="GoTo Page 2" HorizontalAlignment="Center" VerticalAlignment="Center" Tapped="Button_Tapped"/>
    </Grid>
</Page>

BlankPage1.xaml.cs

using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace App2
{
    public sealed partial class BlankPage1 : Page
    {
        public BlankPage1()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (Frame.CanGoBack)
            {
                SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
                SystemNavigationManager.GetForCurrentView().BackRequested += (s, a) =>
                {
                    if (Frame.Content.GetType() == typeof(BlankPage1))
                    {
                        if (Frame.CanGoBack)
                        {
                            Frame.GoBack();
                            a.Handled = true;
                        }
                    }
                };
            }
            else
            {
                SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Collapsed;
            }
        }

        private void Button_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
        {
            Frame.Navigate(typeof(BlankPage2));
        }
    }
}

BlankPage2.xaml

<Page
    x:Class="App2.BlankPage2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Final Page" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</Page>

BlankPage2.xaml.cs

using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace App2
{
    public sealed partial class BlankPage2 : Page
    {
        public BlankPage2()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (Frame.CanGoBack)
            {
                SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
                SystemNavigationManager.GetForCurrentView().BackRequested += (s, a) =>
                {
                    if (Frame.Content.GetType() == typeof(BlankPage2))
                    {
                        if (Frame.CanGoBack)
                        {
                            Frame.GoBack();
                            a.Handled = true;
                        }
                    }
                };
            }
            else
            {
                SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Collapsed;
            }
        }
    }
}

回答1:

Every time you navigate to a page, the OnNavigatedTo method is being called and you're registering a new handler for the BackRequested event, meaning it'll execute multiple times when you press the back button. You should unsubscribe to that event in your OnNavigatedFrom method of each page.

Setting Handled = true doesn't mean that other subscriptions for that event won't be executed, it just means:

If you don't mark the event as handled, the system decides whether to navigate away from the app (on the Mobile device family) or ignore the event (on the Desktop device family).



回答2:

Building up on the answer from @Decade Moon, I would suggest you could even centralize the logic into the App class for easier manageability and cleaner Page code-behinds.

Add the following line to App.OnLaunched method:

SystemNavigationManager.GetForCurrentView().BackRequested += App_BackRequested;

And implement the handler like this:

private void App_BackRequested(object sender, BackRequestedEventArgs e)
{
    var frame = ( Frame )Window.Current.Content;
    if ( frame.CanGoBack )
    {
        frame.GoBack();
        e.Handled = true;
    }
}

We grab the frame of the current window and check if we can navigate back. In case it is possible, we handle the event and navigate. The advantage of this is, that you can now remove all BackRequested related actions on all pages.

You can also do similar thing for the AppViewBackButtonVisibility: add the following to the end of OnLaunched:

rootFrame.Navigated += RootFrame_Navigated;

And now implement the handler as:

private void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
    var frame = (Frame)Window.Current.Content;
    SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
        frame.CanGoBack ? AppViewBackButtonVisibility.Visible :
                          AppViewBackButtonVisibility.Collapsed;            
}

Each time the frame navigates, the visibility of the back button will automatically update.