C# Frame.GoBack()在UI按钮单击时成功,在Windows软件返回按钮回调时失败

C# Frame.GoBack()在UI按钮单击时成功,在Windows软件返回按钮回调时失败,c#,uwp,navigation,windows-10-universal,C#,Uwp,Navigation,Windows 10 Universal,我有一个具有以下基本页面导航结构的语言学习应用程序: 开始-应用程序标题屏幕 语言-语言列表 课程-所选语言的课程列表 活动-包含所选课程的活动的页面 前三个页面各有一个按钮,可通过类似以下的调用导航到下一个页面: private void ButtonClick(object sender, RoutedEventArgs e) => Frame.Navigate(typeof(SomePage)); 在“活动”页面上,当用户按下按钮提交最后一个正确答案后,应用程序将按如下方式返回:

我有一个具有以下基本页面导航结构的语言学习应用程序:

开始-应用程序标题屏幕 语言-语言列表 课程-所选语言的课程列表 活动-包含所选课程的活动的页面 前三个页面各有一个按钮,可通过类似以下的调用导航到下一个页面:

private void ButtonClick(object sender, RoutedEventArgs e) => Frame.Navigate(typeof(SomePage));
在“活动”页面上,当用户按下按钮提交最后一个正确答案后,应用程序将按如下方式返回:

private async void SubmitAnswer_Click(object sender, RoutedEventArgs e)
{
    ...

    if (answerCorrect && allActivitiesComplete)
    {
        Frame.GoBack();
        return;
    }

    ...
}
这是有效的;我回到了课程页面。如果我使用IntelliSense将鼠标悬停在帧上,我会看到BackStack属性的计数为3,前面的每一页都有一个

但是,我还想显示软件后退按钮。为此,我有以下代码:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    ...
    var nav = SystemNavigationManager.GetForCurrentView();
    nav.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
    ...
}
很好,现在在桌面模式下,按钮显示在左上角。但按它做什么都没有。因此,我更新如下:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    ...
    var nav = SystemNavigationManager.GetForCurrentView();
    nav.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
    nav.BackRequested += (x, y) =>
    {
        Frame.GoBack();
    };
    ...
}
这在以下情况下失败:

Frame.backbackback显示计数为0,Frame.CanGoBack为false。为什么此代码流与按钮按下代码流不一致?

请尝试以下操作:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    ...

    Windows.UI.Core.SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
    Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += (s,a) =>
    {

        if (Frame.CanGoBack)
        {
            Frame.GoBack();
            a.Handled = true;
        }
    }
    ...
}

理想情况下,您应该全局连接BackRequested事件,而不是在特定页面的范围内。这是因为如果在OnNavigatedTo上添加事件处理程序,则每次导航页面时都会添加另一个事件处理程序。这意味着单击“后退”按钮将向后导航几次。此外,它还会将附加处理程序的所有页面保留在内存中,这是一种严重的内存泄漏。至少您应该从使用lamda切换到事件处理程序方法,并在OnNavigatedFrom中取消订阅它

要正确实施应用程序标题栏后退按钮,应执行以下操作:

观察框架导航方法,根据CanGoBack属性适当显示/隐藏后退按钮 在BackRequested处理程序中,检查CanGoBack以确保导航是可能的 空白UWP应用程序的实现示例如下:

将以下方法添加到App.xaml.cs:

还添加一个私有的根框架字段:

最后更新OnLaunched方法以存储根帧并设置back按钮:

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        ...

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;

        _rootFrame = rootFrame;
        SetupAppBarBackButton();
    }
    ...
}

请注意,如果您在其他地方创建根框架,以及为其他激活路径创建根框架,您需要存储框架并在其中调用SetupAppBarBackButton方法。

应用程序级别的后退按钮应在应用程序级别注册,或者如果您的应用程序使用导航视图,则在具有导航逻辑的页面中,通常在主页上注册,因此,为了在整个应用程序中保持一致性,导航堆栈和您所指的框架始终相同。另外,在实际调用Frame.GoBack之前,最好检查backbackback在Frame.CanGoBack{//go back}中是否有页面。是的,这就是问题所在。发生此错误的原因是事件处理程序将被创建多次,因此应用程序将导航回超过可能的页面。当时的误解是页面的生命周期。我在App.xaml.cs中全局设置了一次后退按钮,它按预期工作。谢谢很高兴有帮助:-!快乐编码!这帮助我在调试时发现了一些问题;谢谢。请告诉我这是否有帮助,以及您是否需要更多帮助。
private void SetupAppBarBackButton()
{
    _rootFrame.Navigated += RootFrame_Navigated;
    SystemNavigationManager.GetForCurrentView().BackRequested += App_BackRequested;
}

private void App_BackRequested(object sender, BackRequestedEventArgs e)
{
    if (_rootFrame.CanGoBack)
    {
        _rootFrame.GoBack();
    }
}

private void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
    SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
        _rootFrame.CanGoBack
           ? AppViewBackButtonVisibility.Visible
           : AppViewBackButtonVisibility.Collapsed;
}
private Frame _rootFrame;
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        ...

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;

        _rootFrame = rootFrame;
        SetupAppBarBackButton();
    }
    ...
}