前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[UWP 自定义控件]了解模板化控件(5.2):UserControl vs. TemplatedControl

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

作者头像
dino.c
发布2019-01-18 10:54:54
7590
发布2019-01-18 10:54:54
举报

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-04-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. UserControl vs. TemplatedControl
    • 1.1 使用UserControl自定义控件
      • 1.2 使用CustomControl自定义控件
      • 2. 实践:使用UserControl实现DateTimeSelector
      • 3. 混合方案
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档