[UWP 自定义控件]了解模板化控件(5.2):UserControl vs. TemplatedControl
1. UserControl vs. TemplatedControl
在UWP中自定义控件常常会遇到这个问题:使用UserControl还是TemplatedControl来自定义控件。
1.1 使用UserControl自定义控件
- 继承自UserControl。
- 由复数控件组合而成。
- 包含XAML及CodeBehind。
- 优点:
- 上手简单。
- 可以在CodeBehind直接访问UI元素。
- 开发速度很快。
- 缺点:
- 不能使用ControlTemplate进行定制。
- 通常很难继承及扩展。
- 使用场景:
- 需要快速实现一个只有简单功能的控件,而且无需扩展性。
- 不需要可以改变UI。
- 不需要在不同项目中共享控件。
- 使用UserControl的控件:
- Page及DropShadowPanel都是UserControl。
1.2 使用CustomControl自定义控件
- 继承自Control或其派生类。
- 代码和XAML分离,可以没有XAML。
- 可以使用ControlTemplate。
- 控件库中的控件通常都是CustomControl。
- 优点:
- 更加灵活,容易扩展。
- UI和代码分离。
- 缺点:
- 较高的上手难度。
- 使用场景:
- 需要一个可以扩展功能的灵活的控件。
- 需要定制UI。
- 需要在不同的项目中使用。
- 使用CustomControl的控件:
- 控件库中提供的元素,除了直接继承自FrameworkElement的Panel、Shape、TextBlock等少数元素,其它大部分都是CustomControl。
2. 实践:使用UserControl实现DateTimeSelector
上一篇的DateTimeSelector例子很适合讨这个问题。这个控件没有复杂的逻辑,用UserControl的方式实现很简单,代码如下:
public sealed partial class DateTimeSelector3 : UserControl
{
/// <summary>
/// 标识 DateTime 依赖属性。
/// </summary>
public static readonly DependencyProperty DateTimeProperty =
DependencyProperty.Register("DateTime", typeof(DateTime?), typeof(DateTimeSelector3), new PropertyMetadata(null, OnDateTimeChanged));
private static void OnDateTimeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
DateTimeSelector3 target = obj as DateTimeSelector3;
DateTime? oldValue = (DateTime?)args.OldValue;
DateTime? newValue = (DateTime?)args.NewValue;
if (oldValue != newValue)
target.OnDateTimeChanged(oldValue, newValue);
}
public DateTimeSelector3()
{
this.InitializeComponent();
DateTime = System.DateTime.Now;
TimeElement.TimeChanged += OnTimeChanged;
DateElement.DateChanged += OnDateChanged;
}
/// <summary>
/// 获取或设置DateTime的值
/// </summary>
public DateTime? DateTime
{
get { return (DateTime?)GetValue(DateTimeProperty); }
set { SetValue(DateTimeProperty, value); }
}
private bool _isUpdatingDateTime;
private void OnDateTimeChanged(DateTime? oldValue, DateTime? newValue)
{
_isUpdatingDateTime = true;
try
{
if (DateElement != null && DateTime != null)
DateElement.Date = DateTime.Value;
if (TimeElement != null && DateTime != null)
TimeElement.Time = DateTime.Value.TimeOfDay;
}
finally
{
_isUpdatingDateTime = false;
}
}
private void OnDateChanged(object sender, DatePickerValueChangedEventArgs e)
{
UpdateDateTime();
}
private void OnTimeChanged(object sender, TimePickerValueChangedEventArgs e)
{
UpdateDateTime();
}
private void UpdateDateTime()
{
if (_isUpdatingDateTime)
return;
DateTime = DateElement.Date.Date.Add(TimeElement.Time);
}
}
XAML:
<StackPanel>
<DatePicker x:Name="DateElement" />
<TimePicker x:Name="TimeElement"
Margin="0,5,0,0" />
</StackPanel>
代码真的很简单,不需要GetTemplateChild,不需要DefaultStyleKey,不需要Blend,熟练的话大概5分钟就能写好一个。
使用UserControl有这些好处:
- 快速。
- 可以直接查看设计视图,不需要用Blend。
- 可以直接访问XAML中的元素。

当然坏处也不少:
- 不可以通过ControlTemplate修改UI。
- 难以继承并修改。
- UI和代码高度耦合。
如果控件只是内部使用,不是放在类库中向第三者公开,也没有修改的必要,使用UserControl也是合适的,毕竟它符合80/20原则:使用20%的时间完成了80%的功能。
3. 混合方案
如果需要快速实现控件,又需要适当的扩展能力,可以实现一个继承UserControl的基类,再通过UserControl的方式派生这个基类。
public class DateTimeSelectorBase : UserControl
创建一个名为DateTimeSelectorBase的类,继承自UserControl,其它代码基本上照抄上一篇文章中的DatetimeSelector2,只不过删除了构造函数中的代码,因为不需要DefaultStyle。
然后用普通的方式新建一个UserControl,在XAML和CodeBehind中将基类改成DateTimeSelectorBase,如下所示:
<local:DateTimeSelectorBase x:Class="TemplatedControlSample.DateTimeSelector4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TemplatedControlSample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="DateTimeSelector"
d:DesignHeight="300"
d:DesignWidth="400">
<local:DateTimeSelectorBase.Resources>
<local:DateTimeOffsetConverter x:Key="DateTimeOffsetConverter" />
<local:TimeSpanConverter x:Key="TimeSpanConverter" />
</local:DateTimeSelectorBase.Resources>
<StackPanel>
<DatePicker Margin="0,0,0,5"
Date="{Binding Date,ElementName=DateTimeSelector,Mode=TwoWay,Converter={StaticResource DateTimeOffsetConverter}}" />
<TimePicker Time="{Binding Time,ElementName=DateTimeSelector,Mode=TwoWay}" />
</StackPanel>
</local:DateTimeSelectorBase>
public sealed partial class DateTimeSelector4 : DateTimeSelectorBase
{
public DateTimeSelector4()
{
this.InitializeComponent();
}
}
这样既可以在不同的派生类使用不同的UI,也可以使用设计视图,结合了UserControl和TemplatedControl的优点。缺点是不可以使用ControlTemplate,而且不清楚这个控件的开发者会直观地以为这是TemplatedControl,使用上会造成一些混乱。

[UWP 自定义控件]了解模板化控件(5.2):UserControl vs. TemplatedControl的更多相关文章
- UWP 自定义控件:了解模板化控件 系列文章
UWP自定义控件的入门文章 [UWP 自定义控件]了解模板化控件(1):基础知识 [UWP 自定义控件]了解模板化控件(2):模仿ContentControl [UWP 自定义控件]了解模板化控件(2 ...
- [UWP 自定义控件]了解模板化控件(1):基础知识
1.概述 UWP允许开发者通过两种方式创建自定义的控件:UserControl和TemplatedControl(模板化控件).这个主题主要讲述如何创建和理解模板化控件,目标是能理解模板化控件常见的知 ...
- [UWP 自定义控件]了解模板化控件(10):原则与技巧
1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...
- [UWP 自定义控件]了解模板化控件(8):ItemsControl
1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大 ...
- [UWP 自定义控件]了解模板化控件(2):模仿ContentControl
ContentControl是最简单的TemplatedControl,而且它在UWP出场频率很高.ContentControl和Panel是VisualTree的基础,可以说几乎所有VisualTr ...
- [UWP 自定义控件]了解模板化控件(3):实现HeaderedContentControl
1. 概述 来看看这段XMAL: <StackPanel Width="300"> <TextBox Header="TextBox" /&g ...
- [UWP 自定义控件]了解模板化控件(4):TemplatePart
1. TemplatePart TemplatePart(部件)是指ControlTemplate中的命名元素.控件逻辑预期这些部分存在于ControlTemplate中,并且使用protected ...
- [UWP 自定义控件]了解模板化控件(9):UI指南
1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...
- [UWP 自定义控件]了解模板化控件(2.1):理解ContentControl
UWP的UI主要由布局容器和内容控件(ContentControl)组成.布局容器是指Grid.StackPanel等继承自Panel,可以拥有多个子元素的类.与此相对,ContentControl则 ...
随机推荐
- redis的一命令
参考http://redisdoc.com/ 参考http://redis.io/commands 连接操作相关的命令 默认直接连接 远程连接-h 192.168.1.20 -p 6379 ping ...
- JavaScript高级特性-数组
1. JavaScript中的数组 在C++.Java中,数组是一种高效的数据结构,随机访问性能特别好,但是局限性也特别明显,就是数组中存放的数据必须是同一类型的,而在JavaScript中,数组中的 ...
- dell r420 H310/H810阵列配置教程及常见问题
进入H310/H810阵列卡BIOS界面 阵列卡管理快捷键 如何创建RAID0和RAID1和RAID5 阵列修复篇 Foreign(外来)状态的硬盘应如何处理 1.进入H310/H810阵列卡BIOS ...
- 浅谈tidb事务与MySQL事务之间的区别
MySQL是我们日常生活中常见的数据库,他的innodb存储引擎尤为常见,在事务方面使用的是扁平事务,即要么都执行,要么都回滚.而tidb数据库则使用的是分布式事务.两者都能保证数据的高一致性,但是在 ...
- Linux永久修改系统时间
1.date 查看系统时间 2.hwclock --show 查看硬件的时间 3.hwclock --set --date '2017-08-16 17:17:00' 设置硬件时间为17年8月16日1 ...
- Beta冲刺! Day2 - 砍柴
Beta冲刺! Day2 - 砍柴 今日已完成 晨瑶:大致确定了文章推荐的算法思路(Content-based recommender):理清了不少feature的事宜 昭锡:修复了日期选择越界时导致 ...
- [项目实践] 在项目实战中提升代码效率的的一次应用实践-----使用列表解析式输出当前android设备的CPU核数
1.先按照初学者思维代码应该是这样的 #coding:utf-8 import os text = os.popen("adb shell command cat /proc/cpuinfo ...
- Axure RP Pro7.0的key注册码加汉化非破解
上次我们刚分享过Axure RP Pro6.5 key注册码加汉化非破解,我还要分享一个Axure RP Pro7.0的key注册码加汉化,非破解哦. 当然方法还是不变,先用下面的密钥激活.用户名就是 ...
- 1506 传话 (暴力DFS或者Tarjan模板题)
题目描述 Description 一个朋友网络,如果a认识b,那么如果a第一次收到某个消息,那么会把这个消息传给b,以及所有a认识的人. 如果a认识b,b不一定认识a. 所有人从1到n编号,给出所有“ ...
- Rman将数据文件恢复到不同的路径
RMAN> startup nomount connected to target database (not started)Oracle instance started Total Sys ...