[UWP]使用Popup构建UWP Picker
在上一篇博文《[UWP]不那么好用的ContentDialog》中我们讲到了ContentDialog在复杂场景下使用的几个令人头疼的弊端。那么,就让我们在这篇博文里开始愉快的造轮子之旅吧!
首先要向大家说明:这篇博文主要还是写的构建Picker时的思考过程,如果不感兴趣的,可以直接略过这篇,阅读下一篇《[UWP]如何使用Picker实现一个简单的ColorPicker弹窗》。
首先针对上篇文章中讲到的ContentDialog的几个缺点,先来梳理一下我们对新的弹窗层组件的需求:
- 能实现正常Dialog的弹出关闭流程(必须的,不然怎么来替代ContentDialog?);
- 可以支持同时弹出多层弹窗(解决ContentDialog的弊病);
- 适用于MVVM框架,提供给ViewModel层调用接口(越简单越友好越好,松耦合);
- 友好的返回值(方便调用方直接获取想要的结果);
- 要实现ContentDialog类似的可定制化能力(弹出位置,自定义UI,动画等)。
下面的文章里,为了方便描述,我把实现上诉功能的新弹窗层组件称之为Picker(类似于UWP文件选择器的叫法)。
简化一下需求,我们的Picker主要有三个功能规划:
- Picker的调用呈现方式为弹出一个类似于ContentDialog的弹窗,但是可以多层级的调用(即多层弹窗);
- Picker的业务逻辑实现层遵循共同的调用约束(即实现Picker定义的相关接口),基于此约束实现的MVVM页均可实现Picker的调用方式;
- 实现Picker约束的相关类本身实现独立的Pick功能(即调用后返回特定类型值)。
我们简单的类比下,上面对Picker的功能规划其实有点类似于Windows系统中的文件查看逻辑。
在Windows系统中,可以处理某类格式文件(例如.mp4文件)的程序可以向系统申明支持该格式,当系统需要处理这类文件时(例如双击打开.mp4文件),将会调用这个程序(或者询问用户使用哪一个支持该类文件的程序)来处理该文件。
有了明确的功能规划,我们就可以来思考如何实现了。
如何实现Picker的界面层呈现
我们已经说过了,ContentDialog的内部实现其实是依赖Popup,因此我们同样可以用Popup来承载Picker的界面显示。
Popup本身是一个非常基础的组件(UWP中另外一个常用的组件Flyout也是基于Popup实现的),它在UWP应用的可视化树中是处于最顶层的(PopupRoot),且呈现在应用最前面(可以理解为具有最大值的ZIndex)。基于Popup我们可以实现很多不依赖程序主界面的UI功能,并且不受ContentDialog只能同时显示一个的限制。

这样的话就非常简单了,我们可以构建一个ObjectPicker类,它提供类似ContentDialog类中ShowAsync的方法,调用此方法时创建一个新的Popup,然后将我们要显示的界面元素作为其Child添加到界面上(实际上,结合MVVM框架逻辑,我们使用Frame装载目标页面),在用户完成Pick操作后关闭Popup,并且返回操作结果。
呈现过程的核心,三句代码足以:
var popup = new Popup();
//ToDo 装载Picker界面
popup.IsOpen = true;
//ToDo 处理Pick交互操作
popup.IsOpen = false;
当然,涉及到具体实现时,由于我们要考虑到界面呈现的UI交互、位置及动画,实际代码会更多一点。
ObjectPicker类只负责呈现目标页面,它不负责处理任何的业务逻辑。
如何实现Picker的业务逻辑
在我们的规划中,Picker的实际业务逻辑是在Picker页面的逻辑层代码,即ViewModel层中实现的。这样的话,什么时候Picker可以关闭是由其ViewModel决定的。为了使ViewModel的处理结果可以通知到Picker,我们需要让实现Picker功能的ViewModel遵循一个共同的约束。
我们定义这样一个泛型接口:
public interface IObjectPicker<T>
{
event EventHandler<ObjectPickedEventArgs<T>> ObjectPicked;
event EventHandler Canceled;
}
其中T代表这个ViewModel支持的Pick对象类型,当用户完成Pick操作时触发ObjectPicked事件,用户取消Pick操作时触发Canceled事件,ObjectPicker类将在Show的时候注册这两个事件来接受ViewModel的处理结果。
如何优雅的调用Picker
当我们实现了一个Picker以后,如何来调用它呢?使用最简单的方法,直接在需要调用Picker的地方new一个ObjectPicker,然后调用ShowAsync来获取返回值?
这看起来没什么问题,但是实际应用中,我们的Picker可能有很多个(文字输入框,颜色选取器等)。我们希望有一个类似于Windows系统注册表的服务,它提供:
- 不同类型的Picker注册接口及管理;
- 提供简单友好的Picker调用接口,并且返回Pick结果。
这样的话,我们可以简单的实现一个ObjectPickerService,通过它,我们的调用逻辑会非常的简单:
var pickRet = await pickerService.PickSingleObjectAsync<Color>(typeof(TestColorPickerViewModel).FullName);
这个例子可以在我的开源项目HHChaosToolkit中的Picker部分看到(GitHub链接点这里),同时也是我的下篇博文的主要内容。
结尾
这篇文章到这里就结束了,主要还是讲如何构建Picker组件的思考过程,以及我基于对MVVM结构的理解,如何来梳理Picker的架构。下一篇文章我将结合例子介绍一下Picker的实例实现及其调用流程。
写这篇博文着实有些难产,如果有什么地方写的不够好,或者难以理解,欢迎大家指正!谢谢阅读!
[UWP]使用Popup构建UWP Picker的更多相关文章
- Win10 UWP系列:更新UWP时注意的问题——TargetDeviceFamily
前几天把CurrencyExchanger提交到微软参加Master认证,结果没有通过,反馈了一些错误,看来微软检查还是比较仔细的. 错误主要有: Visual feedback helps user ...
- UWP入门教程1——UWP的前世今生
目录 引言 设备族群 UI 和通用输入模式 通用控件和布局面板 工具 自适应扩展 通用输入处理 引言 在本篇文章中,可以掌握以下知识: 设备族群,如何决定目标设备 新的UI控件和新面板帮助你适应不同的 ...
- win10 uwp 如何开始写 uwp 程序
本文告诉大家如何创建一个 UWP 程序. 这是一系列的 uwp 入门博客,所以写的很简单 本文来告诉大家如何创建一个简单的程序 安装 VisualStudio 在开始写 UWP 需要安装 Visual ...
- UWP 新手教程1——UWP的前世今生
文件夹 引言 设备族群 UI 和通用输入模式 通用控件和布局面板 工具 自适应扩展 通用输入处理 引言 在本篇文章中,可以掌握下面知识: 设备族群,怎样决定目标设备 新的UI控件和新面板帮助你适应不同 ...
- [UWP]使用Picker实现一个简单的ColorPicker弹窗
在上一篇博文<[UWP]使用Popup构建UWP Picker>中我们简单讲述了一下使用Popup构建适用于MVVM框架下的弹窗层组件Picker的过程.但是没有应用实例的话可能体现不出P ...
- [UWP]实现一个轻量级的应用内消息通知控件
在UWP应用开发中,我们常常有向用户发送一些提示性消息的需求.这种时候我们一般会选择MessageDialog.ContentDialog或者ToastNotification来完成功能. 但是,我们 ...
- [UWP]了解模板化控件(10):原则与技巧
1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...
- [UWP 自定义控件]了解模板化控件(10):原则与技巧
1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...
- 是时候 UWP 了 !
什么是 UWP? 很多程序员都有一个梦想:希望自己开发的软件能够轻而易举的在所有平台上运行,而不是把同样的需求,用不同的技术.工具重新开发才能够运行在所有平台上.这就是跨平台,很多软件从业者都在为这个 ...
随机推荐
- PHPsocket、CURL、File_get_contents采集
1.socket采集.采用最底层的,它只是建立一个长连接,然后我们自己构造http协议字符串去发送请求.例如想获取这个页面内容(http://tv.youku.com/?spm=a2hww.20023 ...
- TP5 自定义验证器
TP内置验证功能提供两种验证方法 验证器(推荐) $validate = Validate::make([ 'id' => 'require|integer', ]); if ($validat ...
- 【浅说】堆(heap)和栈(stack)区别
在了解堆与栈之前,我们想来了解下程序的内存分配 一个编译的程序占用的内存分为以下几个部分 : 1.栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式 ...
- 移动平台MOBA发热与帧率优化
MOBA项目的优化进入到了第二阶段,千元机,发热严重问题处理,及帧率进一步提升. 回顾之前的优化,当初我的 OPPO R9S不过8-10帧,后来经过了逻辑计算的一些优化后达到10-20帧. 再后来开启 ...
- JavaScript:BOM&DOM
BOM(Browser Object Model): feature: 1.BOM has no relevant standards. 2.The fundamental object of BOM ...
- Linux简单学习
参考自:http://www.runoob.com/linux/linux-tutorial.html 一.是什么 Linux 类Unix操作系统.是一个基于POSIX和UNIX的多用户.多任务.支 ...
- 反射与特性与Tool编写
大多数程序都是用来处理数据的,他们读,写,操作和显示数据,图形也是一种数据. 程序员为某种目的创建和使用一些类型,因此,在设计时必须理解所使用类型的特性. 有关程序及其类型的数据被称为元数据,他们保存 ...
- 查看oracle的执行计划
基于ORACLE的应用系统很多性能问题,是由应用系统SQL性能低劣引起的,所以,SQL的性能优化很重要,分析与优化SQL的性能我们一般通过查看该SQL的执行计划,本文就如何看懂执行计划,以及如何通过分 ...
- gitlab 常用维护命令
GitLab简介 GitLab 是一个用于仓库管理系统的开源项目.使用Git作为代码管理工具,并在此基础上搭建起来的web服务.Github是公共的git仓库,而Gitlab适合于搭建企业内部私有gi ...
- create react app遇到的问题
我现在想的是吧 static 资源和动态 api 来分开处理, static 资源开启 nginx 服务器,api 请求由 express 完成, 现在的问题是开发的时候 proxy 设定将所有的请求 ...