[UWP]使用Popup构建UWP Picker

在上一篇博文《[UWP]不那么好用的ContentDialog》中咱们讲到了ContentDialog在复杂场景下使用的几个使人头疼的弊端。那么,就让咱们在这篇博文里开始愉快的造轮子之旅吧!html

首先要向你们说明:这篇博文主要仍是写的构建Picker时的思考过程,若是不感兴趣的,能够直接略过这篇,阅读下一篇《[UWP]如何使用Picker实现一个简单的ColorPicker弹窗》。git

首先针对上篇文章中讲到的ContentDialog的几个缺点,先来梳理一下咱们对新的弹窗层组件的需求:github

  • 能实现正常Dialog的弹出关闭流程(必须的,否则怎么来替代ContentDialog?);
  • 能够支持同时弹出多层弹窗(解决ContentDialog的弊病);
  • 适用于MVVM框架,提供给ViewModel层调用接口(越简单越友好越好,松耦合);
  • 友好的返回值(方便调用方直接获取想要的结果);
  • 要实现ContentDialog相似的可定制化能力(弹出位置,自定义UI,动画等)。

下面的文章里,为了方便描述,我把实现上诉功能的新弹窗层组件称之为Picker(相似于UWP文件选择器的叫法)。c#

简化一下需求,咱们的Picker主要有三个功能规划:架构

  1. Picker的调用呈现方式为弹出一个相似于ContentDialog的弹窗,可是能够多层级的调用(即多层弹窗);
  2. Picker的业务逻辑实现层遵循共同的调用约束(即实现Picker定义的相关接口),基于此约束实现的MVVM页都可实现Picker的调用方式;
  3. 实现Picker约束的相关类自己实现独立的Pick功能(即调用后返回特定类型值)。

咱们简单的类比下,上面对Picker的功能规划其实有点相似于Windows系统中的文件查看逻辑。框架

在Windows系统中,能够处理某类格式文件(例如.mp4文件)的程序能够向系统申明支持该格式,当系统须要处理这类文件时(例如双击打开.mp4文件),将会调用这个程序(或者询问用户使用哪个支持该类文件的程序)来处理该文件。动画

有了明确的功能规划,咱们就能够来思考如何实现了。code

如何实现Picker的界面层呈现

咱们已经说过了,ContentDialog的内部实现实际上是依赖Popup,所以咱们一样能够用Popup来承载Picker的界面显示。htm

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的实例实现及其调用流程。

写这篇博文着实有些难产,若是有什么地方写的不够好,或者难以理解,欢迎你们指正!谢谢阅读!

相关文章
相关标签/搜索