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的更多相关文章

  1. [UWP 自定义控件]了解模板化控件(5.2):UserControl vs. TemplatedControl

    1. UserControl vs. TemplatedControl 在UWP中自定义控件常常会遇到这个问题:使用UserControl还是TemplatedControl来自定义控件. 1.1 使 ...

  2. [UWP]了解模板化控件(1):基础知识

    1.概述 UWP允许开发者通过两种方式创建自定义的控件:UserControl和TemplatedControl(模板化控件).这个主题主要讲述如何创建和理解模板化控件,目标是能理解模板化控件常见的知 ...

  3. [UWP]了解模板化控件(2):模仿ContentControl

    ContentControl是最简单的TemplatedControl,而且它在UWP出场频率很高.ContentControl和Panel是VisualTree的基础,可以说几乎所有VisualTr ...

  4. [UWP]了解模板化控件(8):ItemsControl

    1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大 ...

  5. [UWP]了解模板化控件(10):原则与技巧

    1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...

  6. [UWP]了解模板化控件(4):TemplatePart

    1. TemplatePart TemplatePart(部件)是指ControlTemplate中的命名元素.控件逻辑预期这些部分存在于ControlTemplate中,并且使用protected ...

  7. [UWP]了解模板化控件(9):UI指南

    1. 使用TemplateSettings统一外观 TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性. 譬如,修改HeaderedCont ...

  8. [UWP]了解模板化控件(5.1):TemplatePart vs. VisualState

    1. TemplatePart vs. VisualState 在前面两篇文章中分别使用了TemplatePart及VisualState的方式实现了相同的功能,其中明显VisualState的方式更 ...

  9. UserControl VS TemplatedControl

    一:首先来看一下UserControl 熟悉XAML的朋友们都知道,当我们创建一个用户控件的时候,VS会自动为我们生成一个XXX.xaml文件和XXX..xaml.cs文件,XAML文件用于进行控件的 ...

随机推荐

  1. AddNewsServlet -servlet处理响应请求

    package com.pb.news.web.servlet; import java.io.File;import java.io.IOException;import java.util.Dat ...

  2. server

  3. CSS3-loading动画(二)

    上次分享了四个CSS3的加载动画,今天继续(标题接上一次). 在线demo:http://liyunpei.xyz/loading.html   (持续更新) 请注意:代码中的关键帧动画有的用的lin ...

  4. 关于Lumen / Laravel .env 文件中的环境变量是如何生效的

    .env 文件包含默认环境变量,我们还可自定义其他任何有效的变量,并可通过  调用 env() 或 $_SERVER 或 $_ENV  来获取该变量.那么env()是如何加载到这些变量的呢?在Lume ...

  5. eclipse上传新项目到GitHub

    1.登录gibhub,创建一个仓库 2.提交本地仓库 右键本地项目 选择git,然后点击next 如图: 再右键项目,commit项目,这是提交到本地git 3.接下来提交到github 输入gith ...

  6. useradd新建用户和权限分配

    场景:在搭建Ftp服务器时候,需要新建ftp用户,其实新建的ftp用户和Linux中root新建的用户一样,只是需要了解新建用户时候的相关规则. 1 解决新建用户缺少配置文件 1.1 新建用户 指定目 ...

  7. (转)Java线程:新特征-条件变量

    Java线程:新特征-条件变量   条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量.但是必须说明,这里的条件是没有实际含义的,仅仅是个标记而已,并且条件的含义往往 ...

  8. Redis各种数据结构性能数据对比和性能优化实践

    很对不起大家,又是一篇乱序的文章,但是满满的干货,来源于实践,相信大家会有所收获.里面穿插一些感悟和生活故事,可以忽略不看.不过听大家普遍的反馈说这是其中最喜欢看的部分,好吧,就当学习之后轻松一下. ...

  9. 构建 MariaDB Galera Cluster 分布式数据库集群(一)

    MariaDB Galera Cluster 介绍 简介 MariaDB集群是MariaDB同步多主机集群,仅支持XtraDB(详见本文结尾注释)/InnoDB存储引擎(虽然有对MyISAM实验支持 ...

  10. 相对路径&绝对路径

    粗浅理解: 1)绝对路径:带有访问协议的路径,就是带上http访问协议 2)相对路径: ①以带斜杠开头的相对路径: A)前台相对路径:以web服务器的根路径为参照路径,比如在jsp.HTML中写的路径 ...