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 ...
随机推荐
- 详解树莓派Model B+控制蜂鸣器演奏乐曲
步进电机以及无源蜂鸣器这些都需要脉冲信号才能够驱动,这里将用GPIO的PWM接口驱动无源蜂鸣器弹奏乐曲,本文基于树莓派Mode B+,其他版本树莓派实现时需参照相关资料进行修改! 1 预备知识 1.1 ...
- 再谈CAAnimation动画
CAAnimaton动画分为CABasicAnimation & CAKeyframeAnimation CABasicAnimation动画, 顾名思义就是最基本的动画, 老规矩先上代码: ...
- 使用 JavaScript 和 canvas 做精确的像素碰撞检测
原文地址:Pixel accurate collision detection with Javascript and Canvas 译者:nzbin 我正在开发一个需要再次使用碰撞检测的游戏.我通常 ...
- nodejs利用http模块实现银行卡所属银行查询和骚扰电话验证
http模块内部封装了http服务器和客户端,因此Node.js不需要借助Apache.IIS.Nginx.Tomcat等传统HTTP服务器,就可以构建http服务器,亦可以用来做一些爬虫.下面简单介 ...
- Asp.Net WebApi核心对象解析(上篇)
生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
- MSYS2——Windows平台下模拟linux环境的搭建
最近从MSYS1.0迁移到了MSYS2.0,简单讲,MSYS2.0功能更强大,其环境模拟更加符合linux.虽然本身来自cygwin,但其集成了pacman软件管理工具,很有linux范,并且可以直接 ...
- jQuery radio的取值与赋值
取值: $("input[name='radioName']:checked").val(); 赋值: $("input[name='radioName'][value= ...
- 如何开发FineReport的自定义控件?
FineReport作为插件化开发的报表软件,有些特殊需求的功能需要自己开发,开发的插件包帆软官方有提提供,可以去帆软论坛上找,本文将主要介绍如何开发一个自定义控件,这里讲讲方法论. 第一步:实例化一 ...
- .NET跨平台之旅:将示例站点升级至 .NET Core 1.1 Preview 1
今天微软发布了 .NET Core 1.1 Preview 1(详见 Announcing .NET Core 1.1 Preview 1 ),紧跟 .NET Core 前进的步伐,我们将示例站点 h ...