前一段开发UWP应用的时候因为系统返回按钮事件(SystemNavigationManager.GetForCurrentView().BackRequested)浪费了不少时间。现象就是在手机版的详细页面跳转到其他应用,然后再返回应用,点击系统的返回按钮时应用关闭而不是返回主页面,如果应用不跳转就没有问题。最后调查发现是注销系统返回按钮事件(SystemNavigationManager.GetForCurrentView().BackRequested)的位置放错了,特在此给各位分享一下。

备注:之所以在各个ViewModel注册注销BackRequested事件,是由于各个页面在返回时处理需求不一样(弹确认对话框等等)。

起因

我们都知道Page中如下方法可以重载:

public sealed partial class SecondPage : Page
{
public SecondPage()
{
this.InitializeComponent();
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
} protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
} protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
}
}

他们的执行顺序如下:

  1. 在此页将要在Frame中显示时:OnNavigatedTo
  2. 当此页即将不再是Frame中的活动页面时:OnNavigatingFrom
  3. 当此页不再在Frame中显示时:OnNavigatedFrom

根据NavigationHelper的做法在MVVMLight里面我们也会做一次封装让Viewmodel可以调用:

#region 进程生命期管理

        private String _pageKey;

        /// <summary>
/// 在当前页上注册此事件以向该页填入
/// 在导航过程中传递的内容以及任何
/// 在从以前的会话重新创建页时提供的已保存状态。
/// </summary>
public event LoadStateEventHandler LoadState;
/// <summary>
/// 在当前页上注册此事件以保留
/// 与当前页关联的状态,以防
/// 应用程序挂起或从导航缓存中丢弃
/// 该页。
/// </summary>
public event SaveStateEventHandler SaveState; /// <summary>
/// 在此页将要在 Frame 中显示时进行调用。
/// 此方法调用 <see cref="LoadState"/>,应在此处放置所有
/// 导航和进程生命周期管理逻辑。
/// </summary>
/// <param name="e">描述如何访问此页的事件数据。Parameter
/// 属性提供要显示的组。</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
this._pageKey = "Page-" + this.Frame.BackStackDepth; if (e.NavigationMode == NavigationMode.New)
{
// 在向导航堆栈添加新页时清除向前导航的
// 现有状态
var nextPageKey = this._pageKey;
int nextPageIndex = this.Frame.BackStackDepth;
while (frameState.Remove(nextPageKey))
{
nextPageIndex++;
nextPageKey = "Page-" + nextPageIndex;
} // 将导航参数传递给新页
if (this.LoadState != null)
{
this.LoadState(this, new LoadStateEventArgs(e.Parameter, null));
}
}
else
{
// 通过将相同策略用于加载挂起状态并从缓存重新创建
// 放弃的页,将导航参数和保留页状态传递
// 给页
if (this.LoadState != null)
{
this.LoadState(this, new LoadStateEventArgs(e.Parameter, (Dictionary<String, Object>)frameState[this._pageKey]));
}
}
} /// <summary>
/// 当此页不再在 Frame 中显示时调用。
/// 此方法调用 <see cref="SaveState"/>,应在此处放置所有
/// 导航和进程生命周期管理逻辑。
/// </summary>
/// <param name="e">描述如何访问此页的事件数据。Parameter
/// 属性提供要显示的组。</param>
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
var pageState = new Dictionary<String, Object>();
if (this.SaveState != null)
{
this.SaveState(this, new SaveStateEventArgs(pageState));
}
frameState[_pageKey] = pageState;
}
#endregion

OnNavigatedFrom中调用SaveState(保存页面状态),OnNavigatedTo中调用LoadState(复原页面状态)。

在ViewModel的LoadState里面可能会写入事件绑定(例:系统返回按钮事件)、通知消息等等,自然的我们将事件、通知消息的注销处理写在SaveState里面。

        public void LoadState(object navParameter, Dictionary<string, object> state)
{
if(state!=null)
{
WelcomeTitle = state[nameof(WelcomeTitle)].ToString();
}
// 注册事件
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested
} public void SaveState(Dictionary<string, object> state)
{
state[nameof(WelcomeTitle)] = WelcomeTitle;
// 注销事件
SystemNavigationManager.GetForCurrentView().BackRequested -= OnBackRequested
}

如果应用之间不跳转确实没有问题,但是应用之间跳转就出现问题(事件无效),为什么?

原因

因为跳转的时候触发应用App.xaml.cs的Suspending事件,从而调用OnSuspending处理:

private async void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
// 保存应用状态
await SuspensionManager.SaveAsync();
deferral.Complete();
}

而SuspensionManager.SaveAsync中最后使用frame.GetNavigationState()方法调用OnNavigatedFrom处理(即SaveState),返回的时候驻留在内存的应用直接复原不执行OnNavigatedTo处理(即LoadState)。这样在OnNavigatedFrom处理(即SaveState)中注销的事件和通知消息就得不到复原而导致应用出问题。(特殊情况:如果应用被内存回收掉的话将会执行OnNavigatedTo处理(即LoadState)而没有问题。)

模拟过程

1,启动应用

2,挂起应用(注意:不是挂起并关闭)

到达OnSuspending处理

3,执行frame.GetNavigationState()方法

直接跳转到OnNavigatedFrom处理(即SaveState)

4,复原应用

OnNavigatedTo处理(即LoadState)没有触发

总结

正是由于应用在挂起的时候会出现这个问题所以应该将事件或者通信消息的注销处理写在OnNavigatingFrom里面。Viewmodel可以封装一个SavingState方法让OnNavigatingFrom调用:

public void SavingState()
{
// 注销事件
SystemNavigationManager.GetForCurrentView().BackRequested -= OnBackRequested
}

UWP开发之Mvvmlight实践八:为什么事件注销处理要写在OnNavigatingFrom中的更多相关文章

  1. UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器、实体手机、PC)中应用的Log等文件

    在开发中或者后期测试乃至最后交付使用的时候,如果应用出问题了我们一般的做法就是查看Log文件.上章也提到了查看Log文件,这章重点讲解下如何查看Log文件?如何找到我们需要的Packages安装包目录 ...

  2. UWP开发之Mvvmlight实践二:Mvvmlight的核心框架MVVM与MVC、MVP的区别(图文详解)

    最近UWP开发在海外很潮流,随着微软收购Xamarin,我们这些C#程序员也可以靠这杆小米枪挑战Android,IOS平台了. 那我们为什么选择MVVM做UWP开发?MVC,MVP,MVVM他们之间到 ...

  3. UWP开发之Mvvmlight实践九:基于MVVM的项目架构分享

    在前几章介绍了不少MVVM以及Mvvmlight实例,那实际企业开发中将以那种架构开发比较好?怎样分层开发才能节省成本? 本文特别分享实际企业项目开发中使用过的项目架构,欢迎参照使用!有不好的地方欢迎 ...

  4. UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理

    最近比较忙有一段时间没有更新了,再接再厉继续分享. 案例下载:https://github.com/NewBLife/UWP/tree/master/SuspendSample 先我们看看App在生命 ...

  5. UWP开发之Mvvmlight实践四:{x:bind}和{Binding}区别详解

    {x:bind}是随着UWP被推出而被添加的,可以说是Win10 UWP开发专有扩展.虽然 {x:Bind} 缺少{Binding} 中的一些功能,但它运行时所花费的时间和使用的内存量均比 {Bind ...

  6. UWP开发之Mvvmlight实践三:简单MVVM实例开发(图文详解付代码)

    在做MVVM各种框架对比之前,我觉得有必要先自己做一个简单的MVVM实现案例比较好,这样就可以看到自己实现的时候有那些不方便的地方.而各种框架又是怎么解决我们这些麻烦的. 案例介绍:用户登录画面,没有 ...

  7. UWP开发之Mvvmlight实践一:如何在项目中添加使用Mvvmlight(图文详解)

    最近一直在做UWP开发,为了节省成本等等接触到MVVMlight,觉得有必要发点时间研究它的用法与实现原理才行.如果有问题的地方或者有好的建议欢迎提出来. 随着移动开发的热门,Mvvmlight在An ...

  8. UWP开发之Mvvmlight实践六:MissingMetadataException解决办法(.Net Native下Default.rd.xml配置问题)

    最近完成一款UWP应用,在手机端测试发布版(Release)的时候应用莫名奇妙的强行关闭,而同样的应用包在PC端一点问题都没有,而且Debug版在两个平台都没有问题,唯独手机的Release版有问题. ...

  9. UWP开发之Template10实践:本地文件与照相机文件操作的MVVM实例(图文付原代码)

    前面[UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理]章节已经提到过Template10,为了认识MvvmLight的区别特做了此实例. 原代码地址:ht ...

随机推荐

  1. 微信应用号(小程序)开发IDE配置(第一篇)

    2016年9月22日凌晨,微信宣布“小程序”问世,当然只是开始内测了,微信公众平台对200个服务号发送了小程序内测邀请.那么什么是“小程序”呢,来看微信之父怎么说 看完之后,相信大家大概都有些明白了吧 ...

  2. .NetCore中的日志(2)集成第三方日志工具

    .NetCore中的日志(2)集成第三方日志工具 0x00 在.NetCore的Logging组件中集成NLog 上一篇讨论了.NetCore中日志框架的结构,这一篇讨论一下.NetCore的Logg ...

  3. 【Win 10 应用开发】应用预启动

    所谓预启动,其实你一看那名字就知道是啥意思了,这是直接译,也找不到比这个叫法更简练的词了.在系统资源允许的情况下(比如电池电量充足,有足够的内存空间),系统会把用户常用的应用程序在后台启动,但不会显示 ...

  4. 关于 devbridge-autocomplete 插件多选操作的实现方法

    目前据我所知最好用的 autocomplete 插件就是 jquery-ui 的 autocomplete 以及 devbridge 的 autocomplete 插件. 我最终选择了 devbrid ...

  5. 苹果强制使用HTTPS传输了怎么办?——关于HTTPS,APP开发者必须知道的事

    WeTest 导读 2017年1月1日起,苹果公司将强制使用HTTPS协议传输.本文通过对HTTPS基础原理和通信过程内容的讲解,介绍APP开发者在这个背景下的应对办法. 几周前,我们在<htt ...

  6. 马里奥AI实现方式探索 ——神经网络+增强学习

    [TOC] 马里奥AI实现方式探索 --神经网络+增强学习 儿时我们都曾有过一个经典游戏的体验,就是马里奥(顶蘑菇^v^),这次里约奥运会闭幕式,日本作为2020年东京奥运会的东道主,安倍最后也已经典 ...

  7. Android实现TCP断点上传,后台C#服务实现接收

    终端实现大文件上传一直都是比较难的技术,其中涉及到后端与前端的交互,稳定性和流量大小,而且实现原理每个人都有自己的想法,后端主流用的比较多的是Http来实现,因为大多实现过断点下载.但稳定性不能保证, ...

  8. 关于Visual Studio 未能加载各种Package包的解决方案

    问题: 打开Visual Studio 的时候,总提示未能加载相应的Package包,有时候还无法打开项目,各种提示 解决方案: 进入用户目录 C:\Users\用户名\AppData\Local\M ...

  9. Flex 布局教程:语法篇

    作者: 阮一峰 网页布局(layout)是CSS的一个重点应用. 布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性.它对于那些特殊布局非常不方便 ...

  10. 软件工程(C编码实践篇)学习心得

    孟繁琛 + 原创作品转载请注明出处 + <软件工程(C编码实践篇)>MOOC课程 http://mooc.study.163.com/course/USTC-1000002006 软件工程 ...