【本系列需要具有一定开发基础】

我们在开发中经常遇到这样的场景:

1.呈现详细信息,且包含一些操作。如:查看原图,支持放大,缩小,多图。

2.执行特定的行为,且要有回执结果。如:选择联系人,选中某图,用户登录。

普遍的解决方案就是封装一个UserControl放到页面里,控制其显隐性。如果功能很少,那无所谓,可稍微复杂一点的,封装成单独的一个页面不是更好吗?还能节省当前页面的资源。此方法能够解决场景1,但是场景2需要回执结果,又该怎么办,总不能用全局变量吧。PageUserControl就是主要解决这些问题而封装,它将包含特定逻辑的页面封装成伪控件,使其可以单独调用,且可以反馈执行结果。

调用方法如图:

PageUserControl

PageUserControl是一个抽象的泛型类,作为封装控件的父类。原理:监听Frame的Navigated事件,利用缓存的两个页面变量,区别出是Forward还是Back,然后分别做传值和取值操作。废话不多说,直接上代码:

    public abstract class PageUserControl<TPage>
where TPage : Page
{
private const string _FrameNameInFramePage = "childrenFrame"; private Frame _frame;
private object _frameContentWhenOpened;
private TPage _page; /// <summary>
/// 获取是否优先呈现在ChildrenFrame中。
/// </summary>
public bool IsChildrenFrameFirst { get; protected set; } #region Methods protected void ShowPage()
{
this.OpenPickerPage();
} protected void ShowPage(object parameter)
{
this.OpenPickerPage(parameter);
} //若需向调用者返回某值,则需要实现此方法。
protected virtual void CommitValue(TPage page)
{
} private void OpenPickerPage(object parameter = null)
{
if (null == _frame)
{
_frame = Window.Current.Content as Frame;
if (null != _frame)
{
//这里是约定MainPage页中childrenFrame是子Frame。
//此方法并非绝对,仍有很多灵活的方法可以扩展,比如附加属性来指定谁是ChildrenFrame。
if (this.IsChildrenFrameFirst && this._frame.CurrentSourcePageType.Equals(typeof(Pages.MainPage)))
{
var framePage = (Pages.MainPage)_frame.Content;
var frameInFramePage = framePage.FindName(_FrameNameInFramePage) as Frame;
if (frameInFramePage != null)
{
this._frame = frameInFramePage;
}
} _frameContentWhenOpened = _frame.Content; _frame.Navigated += OnFrameNavigated;
_frame.NavigationStopped += OnFrameNavigationStopped;
_frame.NavigationFailed += OnFrameNavigationFailed; if (parameter == null)
{
_frame.Navigate(typeof(TPage));
}
else
{
_frame.Navigate(typeof(TPage), parameter);
}
}
}
} private void ClosePickerPage()
{
// 注销事件
if (null != _frame)
{
_frame.Navigated -= OnFrameNavigated;
_frame.NavigationStopped -= OnFrameNavigationStopped;
_frame.NavigationFailed -= OnFrameNavigationFailed; _frame = null;
_frameContentWhenOpened = null;
} //若缓存页面有值,则尝试做提交处理。
if (null != this._page)
{
this.CommitValue(this._page);
this._page = null;
}
} #endregion #region Events private void OnFrameNavigated(object sender, NavigationEventArgs e)
{
//若是Back则做关闭处理,若是Forward则把新页缓存。
if (e.Content == _frameContentWhenOpened)
{
ClosePickerPage();
}
else if (null == this._page)
{
var page = e.Content as TPage; if (page != null)
{
this._page = page;
}
}
} private void OnFrameNavigationFailed(object sender, NavigationFailedEventArgs e)
{
ClosePickerPage();
} private void OnFrameNavigationStopped(object sender, NavigationEventArgs e)
{
ClosePickerPage();
} #endregion
}

以上的代码对Frame做了简单扩展,使其能支持在子Frame中呈现(主要是考虑到UWP的SpiltView),但是采用的固定约束,并不灵活。各位看官可以自行扩展,比如:使用附加属性来标识某一个Frame,这里就不实现了。

 
       PageUserControl泛型类的使用参考如下:
public class ImageChooser : PageUserControl<ImageChooserPage>
{
public ImageChooser()
{
//优先在ChildrenFrame呈现。
base.IsChildrenFrameFirst = true;
} public void Show()
{
base.ShowPage();
} protected override void CommitValue(ImageChooserPage page)
{
base.CommitValue(page); //若标识结果的页面属性值有效,则通过事件抛给调用者。
if (!string.IsNullOrWhiteSpace(page.Value))
{
this.OnCompleted(page.Value);
}
} public event EventHandler<ChooseImageCompletedEventArgs> Completed;
private void OnCompleted(string image)
{
var handler = this.Completed;
if (handler != null)
{
handler(this, new ChooseImageCompletedEventArgs(image));
}
}
} public class ChooseImageCompletedEventArgs : EventArgs
{
public string Image { get; private set; } internal ChooseImageCompletedEventArgs(string image)
{
this.Image = image;
}
}

以上代码是针对需要返回值的场景,如果无须返回值则留空或者不重写CommitValue方法即可。

      注意:调用页和控件页需要对NavigationCacheMode操作如下图,使其保证PageUserControl的页面变量唯一性,具体原因参考MSDN-NavigationCacheMode属性介绍。
        public HomePage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
} protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
if (e.NavigationMode == NavigationMode.Back)
{
this.NavigationCacheMode = NavigationCacheMode.Disabled;
}
}

如何正确应用在MVVM模式中?使用Behavior!

        参考示例代码ListPicker。在本示例代码中封装了一个名为ListPicker的PageUserControl,它接受ItemsSources,ItemTemplate,SelectedItem参数,分别对应ListPickerPage中ListView的相同属性。ShowListPickerAction封装了对ListPicker的调用。
        <Button Content="图片"
Grid.Row="1">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<behaviors:ShowListPickerAction ItemsSource="{Binding Images}" ItemTemplate="{ThemeResource ImageItemTemplate}" ItemsPickedCommand="{Binding ImagePickedCommand}" ItemsPickedInputConverter="{StaticResource ListPickerItemsPickedEventArgsConverter}"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</Button>
        详细实现过程,请参考示例:
        点击打开链接
        https://github.com/rolerzhang/UWP-DevSkills

转载请注明出处。

Windows 10(UWP)开发技巧 - PageUserControl的更多相关文章

  1. Windows 10 UWP开发:如何去掉ListView默认的选中效果

    原文:Windows 10 UWP开发:如何去掉ListView默认的选中效果 开发UWP的时候,很多人会碰到一个问题,就是ListView在被数据绑定之后经常有个默认选中的效果,就像这样: 而且它不 ...

  2. Windows 10 UWP开发:如何不让界面卡死

    http://edi.wang/post/2016/2/18/windows-10-uwp-async-await-ui-thread 关于UI线程 这里我们需要一点关于 UI 线程模型的概念,简单的 ...

  3. Mobilize.Net Silverlight bridge to Windows 10 UWP

    Windows UWP 既 Windows 10 Universal Windows platform,这个微软基于Windows NT内核的个运行时(Runtime)平台,此平台横跨所有的 Wind ...

  4. 修改 Windows 10 UWP 应用任务栏图标

    修改 Windows 10 UWP 应用任务栏图标 Windows 7 时代,修改任务栏图标很简单,右键打开属性,更改图标即可.但步入 Windows 8 之后,随着应用商店 UWP 应用的问世,可以 ...

  5. Windows 10 IoT Serials 1 - 针对Minnow Board MAX的Windows 10 IoT开发环境搭建

    目前,微软针对Windows IoT计划支持的硬件包括树莓派2,Minnow Board MAX 和Galileo (Gen 1和Gen 2).其中,Galileo (Gen 1和Gen 2)运行的是 ...

  6. 打造理想的Windows 10 APP开发环境的5个步骤

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:微软即将发布Windows 10手机版,实际上很多人现在已经开始在开发Windows ...

  7. DevExpress Windows 10 UWP Controls新版亮点

    行业领先的.NET界面控件2018年第二次重大更新——DevExpress v18.2日前正式发布,本站将以连载的形式为大家介绍新版本新功能.本文将介绍了DevExpress Windows 10 U ...

  8. DevExpress v18.1新版亮点——Windows 10 UWP篇

    用户界面套包DevExpress v18.1日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress Windows 10 UWP v18.1 的新功能,快来下载 ...

  9. 面向初学者的 Windows 10 UWP 应用开发

    眼看 Windows 10 for Mobile 正式版也快要推送了,就先挖个坑吧,原文视频链接为:Windows 10 development for absolute beginners,以下博客 ...

  10. 基于Prism.Windows的UWP开发备忘

    以前做UWP开发都是使用MvvmLight,主要是简单易上手,同时也写了很多MvvmLight的开发系列文章: UWP开发必备以及常用知识点总结 UWP开发之Mvvmlight实践九:基于MVVM的项 ...

随机推荐

  1. 【Keil5 MDK】fromelf工具的基本用法(fromelf --help)

    ARM FromELF, 5.03 [Build 76] [MDK-ARM Standard] ARM image conversion utilityfromelf [options] input_ ...

  2. 【EMV L2】终端验证结果(Terminal Verification Results,TVR)

    终端验证结果,Terminal Verification Results(TVR),Tag95,5bytes: 记录交易过程中,数据认证.处理限制.持卡人验证.终端风险管理.行为分析以及联机处理的结果 ...

  3. SQLI DUMB SERIES-16

    (1)无论怎么输入,都没有回显,但当输入 admin")#时,显示登录成功.若通过其他方法获取数据库的用户名,可通过这个方法登录成功. (2)获取其他信息可用考虑时间延迟注入.方法同15.

  4. Python中的变量作用域

    python中变量作用域包括: L (Local) 局部作用域,函数内部声明但没有使用global的变量E (Enclosing) 闭包函数外的函数中,def或者lambda的本地作用域G (Glob ...

  5. 本博客不再更新和维护,后续文章会在掘金和GitHub发布,感兴趣的小伙伴可以掘金搜索王振宇,谢谢

    本博客不再更新和维护,后续文章会在掘金和GitHub发布,感兴趣的小伙伴可以掘金搜索王振宇,谢谢

  6. rem,em,与px的比较用法

    在Web中使用什么单位来定义页面的字体大小,至今天为止都还在激烈的争论着,有人说PX做为单位好,有人说EM优点多,还有人在说百分比方便,以至于出现了CSS Font-Size: em vs. px v ...

  7. Eclipse 运行弹出A Java Exception has occurred.并报错Exception in thread 的解决方案

    这个问题是由较高版本的JDK编译的java class文件试图在较低版本的JVM上运行而产生的错误. 1.解决措施就是保证jvm(java命令)和jdk(javac命令)版本一致.如果是linux版本 ...

  8. 简述grub启动引导程序配置及命令行接口详解

    一.版本 grub:Grand Unified Bootloader grub 0.x:grub legacy grub 1.x:grub2 二.grub legacy 三个过程 stage1:安装在 ...

  9. find命令简单使用

    find命令是Linux系统查找文件的命令,能帮助用户在使用.管理Linux的日常事务时方便的查找出用户所需要的文件,find命令基本格式是:find [路径] [选项] [操作]. 列举一下find ...

  10. CRC16 的生成及校验原理

    参考:https://blog.csdn.net/niepangu/article/details/45499383 计算CRC的过程,就是用一个特殊的“除法”,来得到余数,这个余数就是CRC. 它不 ...