UWP开发之Mvvmlight实践八:为什么事件注销处理要写在OnNavigatingFrom中
前一段开发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);
}
}
他们的执行顺序如下:
- 在此页将要在Frame中显示时:OnNavigatedTo
- 当此页即将不再是Frame中的活动页面时:OnNavigatingFrom
- 当此页不再在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中的更多相关文章
- UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器、实体手机、PC)中应用的Log等文件
在开发中或者后期测试乃至最后交付使用的时候,如果应用出问题了我们一般的做法就是查看Log文件.上章也提到了查看Log文件,这章重点讲解下如何查看Log文件?如何找到我们需要的Packages安装包目录 ...
- UWP开发之Mvvmlight实践二:Mvvmlight的核心框架MVVM与MVC、MVP的区别(图文详解)
最近UWP开发在海外很潮流,随着微软收购Xamarin,我们这些C#程序员也可以靠这杆小米枪挑战Android,IOS平台了. 那我们为什么选择MVVM做UWP开发?MVC,MVP,MVVM他们之间到 ...
- UWP开发之Mvvmlight实践九:基于MVVM的项目架构分享
在前几章介绍了不少MVVM以及Mvvmlight实例,那实际企业开发中将以那种架构开发比较好?怎样分层开发才能节省成本? 本文特别分享实际企业项目开发中使用过的项目架构,欢迎参照使用!有不好的地方欢迎 ...
- UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理
最近比较忙有一段时间没有更新了,再接再厉继续分享. 案例下载:https://github.com/NewBLife/UWP/tree/master/SuspendSample 先我们看看App在生命 ...
- UWP开发之Mvvmlight实践四:{x:bind}和{Binding}区别详解
{x:bind}是随着UWP被推出而被添加的,可以说是Win10 UWP开发专有扩展.虽然 {x:Bind} 缺少{Binding} 中的一些功能,但它运行时所花费的时间和使用的内存量均比 {Bind ...
- UWP开发之Mvvmlight实践三:简单MVVM实例开发(图文详解付代码)
在做MVVM各种框架对比之前,我觉得有必要先自己做一个简单的MVVM实现案例比较好,这样就可以看到自己实现的时候有那些不方便的地方.而各种框架又是怎么解决我们这些麻烦的. 案例介绍:用户登录画面,没有 ...
- UWP开发之Mvvmlight实践一:如何在项目中添加使用Mvvmlight(图文详解)
最近一直在做UWP开发,为了节省成本等等接触到MVVMlight,觉得有必要发点时间研究它的用法与实现原理才行.如果有问题的地方或者有好的建议欢迎提出来. 随着移动开发的热门,Mvvmlight在An ...
- UWP开发之Mvvmlight实践六:MissingMetadataException解决办法(.Net Native下Default.rd.xml配置问题)
最近完成一款UWP应用,在手机端测试发布版(Release)的时候应用莫名奇妙的强行关闭,而同样的应用包在PC端一点问题都没有,而且Debug版在两个平台都没有问题,唯独手机的Release版有问题. ...
- UWP开发之Template10实践:本地文件与照相机文件操作的MVVM实例(图文付原代码)
前面[UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理]章节已经提到过Template10,为了认识MvvmLight的区别特做了此实例. 原代码地址:ht ...
随机推荐
- MVVM模式和在WPF中的实现(一)MVVM模式简介
MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...
- DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对?
写在前面 阅读目录: 具体业务场景 业务需求变化 "愚蠢"的应对 消息列表实现 消息详情页实现 消息发送.回复.销毁等实现 回到原点的一些思考 业务需求变化,领域模型变化了吗? 对 ...
- Hyper-V 激活Windows系统重启后黑屏的解决方法 + 激活方法
异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 服务器相关的知识点:http://www.cnblogs.com/dunitia ...
- 百度 flash html5自切换 多文件异步上传控件webuploader基本用法
双核浏览器下在chrome内核中使用uploadify总有302问题,也不知道如何修复,之所以喜欢360浏览器是因为帮客户控制渲染内核: 若页面需默认用极速核,增加标签:<meta name=& ...
- android http 抓包
有时候想开发的时候想看APP发出的http请求和响应是什么,这就需要抓包了,这可以得到一些不为人知的api,比如还可以干些“坏事”... 需要工具: Fiddler2 抓包(点击下载) Android ...
- [原][Docker]特性与原理解析
Docker特性与原理解析 文章假设你已经熟悉了Docker的基本命令和基本知识 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上, ...
- Ajax部分
Ajax的概念 AJAX即"Asynchronous Javascript And XML"(异步JavaScript和XML),是一种用于创建快速动态网页的技术. 动态网页:是指 ...
- Android中使用ExpandableListView实现微信通讯录界面(完善仿微信APP)
之前的博文<Android中使用ExpandableListView实现好友分组>我简单介绍了使用ExpandableListView实现简单的好友分组功能,今天我们针对之前的所做的仿微信 ...
- 微信小程序监控 - HotApp统计
HotApp小程序统计,第一个专业的微信第三方小程序监控统计工具 1.什么是HotApp小程序统计 HotApp小程序统计是第一个微信第三方小程序统计工具,就像做android 和 ios开发的人知道 ...
- 一个简单的网站web项目的详解
有不对的术语,或者不好理解的部分,欢迎大家批评指正,谢谢大家! 近期做的网站web项目,实现登录功能,查询功能.首先把这个项目分为几个模块来处理,当前用户模块,历史用户模块,历史记录模块,数据库模块, ...