[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 自定义控件]了解模板化控件(5.2):UserControl vs. TemplatedControl
1. UserControl vs. TemplatedControl 在UWP中自定义控件常常会遇到这个问题:使用UserControl还是TemplatedControl来自定义控件. 1.1 使 ...
- [UWP]了解模板化控件(1):基础知识
1.概述 UWP允许开发者通过两种方式创建自定义的控件:UserControl和TemplatedControl(模板化控件).这个主题主要讲述如何创建和理解模板化控件,目标是能理解模板化控件常见的知 ...
- [UWP]了解模板化控件(2):模仿ContentControl
ContentControl是最简单的TemplatedControl,而且它在UWP出场频率很高.ContentControl和Panel是VisualTree的基础,可以说几乎所有VisualTr ...
- [UWP]了解模板化控件(8):ItemsControl
1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大 ...
- [UWP]了解模板化控件(10):原则与技巧
1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...
- [UWP]了解模板化控件(4):TemplatePart
1. TemplatePart TemplatePart(部件)是指ControlTemplate中的命名元素.控件逻辑预期这些部分存在于ControlTemplate中,并且使用protected ...
- [UWP]了解模板化控件(9):UI指南
1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...
- [UWP]了解模板化控件(5.1):TemplatePart vs. VisualState
1. TemplatePart vs. VisualState 在前面两篇文章中分别使用了TemplatePart及VisualState的方式实现了相同的功能,其中明显VisualState的方式更 ...
- UserControl VS TemplatedControl
一:首先来看一下UserControl 熟悉XAML的朋友们都知道,当我们创建一个用户控件的时候,VS会自动为我们生成一个XXX.xaml文件和XXX..xaml.cs文件,XAML文件用于进行控件的 ...
随机推荐
- JavaScript 语言中的 this
JavaScript 语言中的 this 由于其运行期绑定的特性,JavaScript 中的 this 含义要丰富得多,它可以是全局对象.当前对象或者任意对象,这完全取决于函数的调用方式.JavaSc ...
- 跳跳棋(9018_1563)(BZOJ_2144)
题目: Hzwer的跳跳棋是在一条数轴上进行的.棋子只能摆在整点上.每个点不能摆超过一个棋子. 某一天,黄金大神和cjy用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置.他们要 ...
- TCP错误恢复特性之一TCP重传
TCP的错误恢复特性是我们用来定位.诊断并最终修复网络高延迟的最好工具. 常见的TCP错误恢复特性有:TCP重传.TCP重复确认和快速重传 1. TCP重传: 重传数据包是TCP最基本的错误恢复特性之 ...
- 【使用WCF,发布服务端浏览报错】未能从程序集“System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089” 中加载类型 “System.ServiceModel.Activation.HttpModule”。
问题: 在WIN7中的IIS服务器中部署WCF服务程序时,通过浏览器访问报出如下错误: 未能从程序集"System.ServiceModel, Version=3.0.0.0, Cultur ...
- css 定位属性position的使用方法实例-----一个层叠窗口
运行结果: <!DOCTYPE html> <html> <head> <title>重叠样式窗口</title> <style ty ...
- 如何查看安装的sql server是什么版本
方法 1:通过使用 SQL Server Management Studio 中的对象资源管理器连接到服务器.连接对象资源管理器后,它将显示版本信息(在括号中),以及用于连接到 SQL Server ...
- requireJS 源码(三) data-main 的加载实现
(一)入口 通过 data-main 去加载 JS 模块,是通过 req(cfg) 入口去进行处理的. 为了跟踪,你可以在此 加断点 进行调试跟踪. (二) req({ })执行时,functio ...
- MongoDB数据库聚合
前面的话 聚合操作主要用于对数据的批量处理,将记录按条件分组以后,然后再进行一系列操作,例如,求最大值.最小值.平均值,求和等操作.聚合操作还能够对记录进行复杂的操作,主要用于数理统计和数据挖掘.在 ...
- Palindrome 回文数
回文数,从前到后,从后到前都一样 把数字转成字符串来处理 package com.rust.cal; public class Palindrome { public static boolean i ...
- 谈一款MOBA游戏《码神联盟》的服务端架构设计与实现
一.前言 <码神联盟>是一款为技术人做的开源情怀游戏,每一种编程语言都是一位英雄.客户端和服务端均使用C#开发,客户端使用Unity3D引擎,数据库使用MySQL.这个MOBA类游戏是笔者 ...