zoukankan      html  css  js  c++  java
  • [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,使用上会造成一些混乱。

  • 相关阅读:
    leetcode Super Ugly Number
    leetcode Find Median from Data Stream
    leetcode Remove Invalid Parentheses
    leetcode Range Sum Query
    leetcode Range Sum Query
    leetcode Minimum Height Trees
    hdu 3836 Equivalent Sets
    hdu 1269 迷宫城堡
    hud 2586 How far away ?
    poj 1330 Nearest Common Ancestors
  • 原文地址:https://www.cnblogs.com/dino623/p/UserControlvsTemplatedControl.html
Copyright © 2011-2022 走看看