在WP8中,处理后退键比较简单,重写OnBackKeyPress事件即可。如经常用的双击后退键退出的功能,用户在MainPage页面第一次点击后退键时,弹出一个对话框"是否退出?",在短时间内如两秒钟内再次点击后退键则退出,否则不退出。只要处理e.Cancel值为true即可取消后退键的默认操作。代码如下:
private DateTime dtBackTimeFirst; private DateTime dtBackTimeSecond;
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e) { dtBackTimeSecond = System.DateTime.Now; TimeSpan ts = dtBackTimeSecond - dtBackTimeFirst; if (ts >= new TimeSpan(0, 0, 2)) { UIService.Instance.ShowToastPrompt("8-)", AppResources.App_Toast_PressBackToExit_Message); e.Cancel = true; dtBackTimeFirst = dtBackTimeSecond; } else { base.OnBackKeyPress(e); }
} |
到了WP8.1中,方式就变了,后退键的事件变成了Windows.Phone.UI.Input.HardwareButtons.BackPressed,WP8.1的导航和WP8是不同的,如果在页面不做处理的话,点击后退键就直接后台了,而不是返回之前的页面。因此这个事件必须单独进行处理,好在VS的模板已经添加了一个NavigationHelper的类,帮助处理导航。除了MainPage页面,添加其他页面时,VS会在页面的构造函数中自动注册这个类来帮助导航:代码如下:
this.navigationHelper = new NavigationHelper(this); this.navigationHelper.LoadState += this.NavigationHelper_LoadState; this.navigationHelper.SaveState += this.NavigationHelper_SaveState; |
下面还有:
/// <summary> /// Populates the page with content passed during navigation. Any saved state is also /// provided when recreating a page from a prior session. /// </summary> /// <param name="sender"> /// The source of the event; typically <see cref="NavigationHelper"/> /// </param> /// <param name="e">Event data that provides both the navigation parameter passed to /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested and /// a dictionary of state preserved by this page during an earlier /// session. The state will be null the first time a page is visited.</param> private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e) { //Let Viewmodel Handle the Load/Save State Logic base.LoadState(e.NavigationParameter, e.PageState); }
/// <summary> /// Preserves state associated with this page in case the application is suspended or the /// page is discarded from the navigation cache. Values must conform to the serialization /// requirements of <see cref="SuspensionManager.SessionState"/>. /// </summary> /// <param name="sender">The source of the event; typically <see cref="NavigationHelper"/></param> /// <param name="e">Event data that provides an empty dictionary to be populated with /// serializable state.</param> private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e) { //Let Viewmodel Handle the Load/Save State Logic base.SaveState(e.PageState); }
#region NavigationHelper registration
/// The methods provided in this section are simply used to allow /// NavigationHelper to respond to the page's navigation methods. /// /// Page specific logic should be placed in event handlers for the /// <see cref="GridCS.Common.NavigationHelper.LoadState"/> /// and <see cref="GridCS.Common.NavigationHelper.SaveState"/>. /// The navigation parameter is available in the LoadState method /// in addition to page state preserved during an earlier session.
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); navigationHelper.OnNavigatedTo(e); }
protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); navigationHelper.OnNavigatedFrom(e); }
#endregion |
这样我们就不用手动处理后退键了,在其他页面中点击后退键会自动返回到上一页。
那么需要继续实现双击退出提示,就需要手动注册HardwareButtons.BackPressed事件了。代码修改为:
protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); navigationHelper.OnNavigatedTo(e); Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtonsOnBackPressed; } protected override void OnNavigatedFrom(NavigationEventArgs e) { Windows.Phone.UI.Input.HardwareButtons.BackPressed -= HardwareButtonsOnBackPressed; base.OnNavigatedFrom(e); navigationHelper.OnNavigatedFrom(e); } |
然后处理后退键:
private DateTime dtBackTimeFirst; private DateTime dtBackTimeSecond; protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e) {
if (CommonAppSettings.IsEnableDoubleBackKeyPressToExit) { dtBackTimeSecond = System.DateTime.Now; TimeSpan ts = dtBackTimeSecond - dtBackTimeFirst; if (ts >= new TimeSpan(0, 0, 2)) { UIService.Instance.ShowToastPrompt("", "再按一次返回键退出程序 8-)"); e.Cancel = true; dtBackTimeFirst = dtBackTimeSecond; } else { base.OnBackKeyPress(e); } } else { base.OnBackKeyPress(e); }
} |
这样就行了。
但是在处理二级页面的时候,遇到了一点问题。在文章详情页面,我放了一个隐藏的Grid,用来显示评论列表。当评论列表显示的时候,点击后退键应该是隐藏评论列表,而不是返回首页。于是我想当然的也处理HardwareButtons.BackPressed事件:
private void HardwareButtonsOnBackPressed(object sender, BackPressedEventArgs e) { var vm = this.LayoutRoot.DataContext as ArticleDetailPage_Model; if (vm != null) { if (vm.IsShowComments) { e.Handled = true; vm.IsShowComments = false; } }
} |
但是,没起作用,设置了e.Handled = true后页面仍然返回了首页。
为什么会发生这种情况呢?于是看NavigationHelper这个类的代码,这个类里注册了HardwareButtons.BackPressed事件,是放在Windows Phone的条件编译里:
/// <summary> /// Invoked when the hardware back button is pressed. For Windows Phone only. /// </summary> /// <param name="sender">Instance that triggered the event.</param> /// <param name="e">Event data describing the conditions that led to the event.</param> private void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e) { if (this.GoBackCommand.CanExecute(null)) { e.Handled = true; this.GoBackCommand.Execute(null); } } |
原因找到了,虽然我在页面里手动处理了HardwareButtons.BackPressed事件,但因为NavigationHelper 仍然在起作用,因此仍然执行了GoBackCommand,于是又后退了。
那么修改一下,只有当e.Handled为false的时候再让NavigationHelper起作用吧:
private void HardwareButtons_BackPressed(object sender, Windows.Phone.UI.Input.BackPressedEventArgs e) { if (this.GoBackCommand.CanExecute(null) && !e.Handled) { e.Handled = true; this.GoBackCommand.Execute(null); } } |
当执行到这里时,因为我们之前已经手动让e.Handled为true了,所以这里就不执行了,达到了显示评论列表的时候点击后退键隐藏列表而不是返回上一页的目的。