Frame.GoBack() 在 UI 按钮点击时成功,在 Windows 软件后退按钮回调时失败
Frame.GoBack() succeeds on UI button click, fails on Windows software back button callback
我有一个具有以下基本页面导航结构的 language-learning 应用程序:
- 开始 - 应用程序标题屏幕
- 语言 - 语言列表
- 课程 - 所选语言的课程列表
- 活动 - 包含所选课程的活动的页面
前三个页面各有一个按钮,可通过类似于以下的调用导航到下一页:
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 将鼠标悬停在 Frame
上,我会看到 BackStack
属性 的计数为 3,每个前面的页面一个。
不过,我也想显示软件后退按钮。为此,我有以下代码:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
...
var nav = SystemNavigationManager.GetForCurrentView();
nav.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
...
}
很好,现在按钮显示在 desktop-mode 的左上角。但是按下它没有任何作用。所以我更新如下:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
...
var nav = SystemNavigationManager.GetForCurrentView();
nav.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
nav.BackRequested += (x, y) =>
{
Frame.GoBack();
};
...
}
这个失败:
Error HRESULT E_FAIL has been returned from a call to a COM component.
Frame.BackStack
显示计数 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
.
中取消订阅
要正确实现应用标题栏后退按钮,您应该这样做:
- 遵守
Frame
Navigated
方法,根据CanGoBack
属性 酌情show/hide后退按钮
- 在
BackRequested
处理程序中检查 CanGoBack
以确保可以导航
空白 UWP 应用的实施示例如下:
将以下方法添加到您的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;
}
同时添加一个私有 _rootFrame
字段:
private Frame _rootFrame;
最后更新 OnLaunched
方法来存储根框架并设置后退按钮:
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
方法。
我有一个具有以下基本页面导航结构的 language-learning 应用程序:
- 开始 - 应用程序标题屏幕
- 语言 - 语言列表
- 课程 - 所选语言的课程列表
- 活动 - 包含所选课程的活动的页面
前三个页面各有一个按钮,可通过类似于以下的调用导航到下一页:
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 将鼠标悬停在 Frame
上,我会看到 BackStack
属性 的计数为 3,每个前面的页面一个。
不过,我也想显示软件后退按钮。为此,我有以下代码:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
...
var nav = SystemNavigationManager.GetForCurrentView();
nav.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
...
}
很好,现在按钮显示在 desktop-mode 的左上角。但是按下它没有任何作用。所以我更新如下:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
...
var nav = SystemNavigationManager.GetForCurrentView();
nav.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
nav.BackRequested += (x, y) =>
{
Frame.GoBack();
};
...
}
这个失败:
Error HRESULT E_FAIL has been returned from a call to a COM component.
Frame.BackStack
显示计数 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
.
要正确实现应用标题栏后退按钮,您应该这样做:
- 遵守
Frame
Navigated
方法,根据CanGoBack
属性 酌情show/hide后退按钮
- 在
BackRequested
处理程序中检查CanGoBack
以确保可以导航
空白 UWP 应用的实施示例如下:
将以下方法添加到您的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;
}
同时添加一个私有 _rootFrame
字段:
private Frame _rootFrame;
最后更新 OnLaunched
方法来存储根框架并设置后退按钮:
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
方法。