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

来源:互联网 发布:java语言培训学校 编辑:程序博客网 时间:2024/05/29 19:14

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,使用上会造成一些混乱。

0 0
原创粉丝点击