博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[UWP]了解模板化控件(5.1):TemplatePart vs. VisualState
阅读量:5785 次
发布时间:2019-06-18

本文共 7735 字,大约阅读时间需要 25 分钟。

原文:

1. TemplatePart vs. VisualState

在前面两篇文章中分别使用了TemplatePart及VisualState的方式实现了相同的功能,其中明显VisualState的方式更灵活一些。如果遇到这种情况通常我更倾向使用VisualState。不过在实际应用中这两种实现方式并不是互斥的,很多模板化控件都同时使用这两种方式,

使用VisualState有如下好处:

  • 代码和UI分离。
  • 可以更灵活地扩展控件。
  • 可以使用Blend轻松实现动画。

并不是说VisualState好处这么多就一定要用VisualState实现所有功能,下面这些情况我会选择使用TemplatePart:

  • 需要快速实现一个控件。
  • 某个行为时固定的,不需要扩展。
  • 需要在代码中操作UI,譬如Slider或ComboBox。
  • 为了强调某个部件是控件必须的。
  • 为了隐藏实现细节,限制派生类或ControlTemplate修改重要的逻辑。

其中,使用TemplatePart产生的扩展性问题是我谨慎使用这种方案的最大因素。

2. TemplatePart vs. TemplateBinding

除了VisualState,TemplatePart的功能也常常会被TemplateBinding代替。前面的例子展示了使用VisualState在UI上的优势,这次用另一个控件DateTimeSelector来讨论使用TemplatePart在扩展性上的其它问题。

2.1 使用TemplatePart

DateTimeSelector组合了CalendarDatePicker和TimePicker,用于选择日期和时间(SelectedDateTime)。它的XAML如下:

代码如下:

[TemplatePart(Name = DateElementPartName, Type = typeof(CalendarDatePicker))][TemplatePart(Name = TimeElementPartName, Type = typeof(TimePicker))]public class DateTimeSelector : Control{    public const string DateElementPartName = "DateElement";    public const string TimeElementPartName = "TimeElement";    ///     /// 标识 SelectedDateTime 依赖属性。    ///     public static readonly DependencyProperty SelectedDateTimeProperty =        DependencyProperty.Register("SelectedDateTime", typeof(DateTime), typeof(DateTimeSelector), new PropertyMetadata(DateTime.Now, OnSelectedDateTimeChanged));    private static void OnSelectedDateTimeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)    {        DateTimeSelector target = obj as DateTimeSelector;        DateTime oldValue = (DateTime)args.OldValue;        DateTime newValue = (DateTime)args.NewValue;        if (oldValue != newValue)            target.OnSelectedDateTimeChanged(oldValue, newValue);    }    public DateTimeSelector()    {        this.DefaultStyleKey = typeof(DateTimeSelector);    }    ///     /// 获取或设置SelectedDateTime的值    ///       public DateTime SelectedDateTime    {        get { return (DateTime)GetValue(SelectedDateTimeProperty); }        set { SetValue(SelectedDateTimeProperty, value); }    }    private CalendarDatePicker _dateElement;    private TimePicker _timeElement;    private bool _isUpdatingDateTime;    protected override void OnApplyTemplate()    {        base.OnApplyTemplate();        if (_dateElement != null)            _dateElement.DateChanged -= OnDateElementDateChanged;        _dateElement = GetTemplateChild(DateElementPartName) as CalendarDatePicker;        if (_dateElement != null)            _dateElement.DateChanged += OnDateElementDateChanged;        if (_timeElement != null)            _timeElement.TimeChanged -= OnTimeElementTimeChanged;        _timeElement = GetTemplateChild(TimeElementPartName) as TimePicker;        if (_timeElement != null)            _timeElement.TimeChanged += OnTimeElementTimeChanged;        UpdateElement();    }    protected virtual void OnSelectedDateTimeChanged(DateTime oldValue, DateTime newValue)    {        UpdateElement();    }    private void OnDateElementDateChanged(CalendarDatePicker sender, CalendarDatePickerDateChangedEventArgs args)    {        UpdateSelectDateTime();    }    private void OnTimeElementTimeChanged(object sender, TimePickerValueChangedEventArgs e)    {        UpdateSelectDateTime();    }    private void UpdateElement()    {        _isUpdatingDateTime = true;        try        {            if (_dateElement != null)                _dateElement.Date = SelectedDateTime.Date;            if (_timeElement != null)                _timeElement.Time = SelectedDateTime.TimeOfDay;        }        finally        {            _isUpdatingDateTime = false;        }    }    private void UpdateSelectDateTime()    {        if (_isUpdatingDateTime)            return;        DateTime dateTime = DateTime.Now;        if (_dateElement != null && _dateElement.Date.HasValue)            dateTime = _dateElement.Date.Value.Date;        if (_timeElement != null)            dateTime = dateTime.Add(_timeElement.Time);        SelectedDateTime = dateTime;    }}

可以看出,DateTimeSelector通过监视CalendarDatePicker的DateChanged和TimePicker的TimeChanged来改变SelectedDateTime的值。

DateTimeSelector的代码很简单,控件也工作得很好,但如果某天需要将CalendarDatePicker 替换为DatePicker或某个第三方的日期选择控件,DateTimeSelector就无能为力了,既不能通过修改ControlTemplate,也不能通过继承来达到目的。

2.2. 使用TemplateBinding

通常在构建这类控件时应先考虑它的数据和行为,而不关心它的UI。DateTimeSelector最核心的功能是通过选择Date和Time得出组合起来的DateTime,那么就可以先写出如下的类:

public class DateTimeSelector2 : Control{    ///     /// 标识 Date 依赖属性。    ///     public static readonly DependencyProperty DateProperty =        DependencyProperty.Register("Date", typeof(DateTime), typeof(DateTimeSelector2), new PropertyMetadata(DateTime.Now, OnDateChanged));    private static void OnDateChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)    {        DateTimeSelector2 target = obj as DateTimeSelector2;        DateTime oldValue = (DateTime)args.OldValue;        DateTime newValue = (DateTime)args.NewValue;        if (oldValue != newValue)            target.OnDateChanged(oldValue, newValue);    }    ///     /// 标识 Time 依赖属性。    ///     public static readonly DependencyProperty TimeProperty =        DependencyProperty.Register("Time", typeof(TimeSpan), typeof(DateTimeSelector2), new PropertyMetadata(TimeSpan.Zero, OnTimeChanged));    private static void OnTimeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)    {        DateTimeSelector2 target = obj as DateTimeSelector2;        TimeSpan oldValue = (TimeSpan)args.OldValue;        TimeSpan newValue = (TimeSpan)args.NewValue;        if (oldValue != newValue)            target.OnTimeChanged(oldValue, newValue);    }    ///     /// 标识 DateTime 依赖属性。    ///     public static readonly DependencyProperty DateTimeProperty =        DependencyProperty.Register("DateTime", typeof(DateTime), typeof(DateTimeSelector2), new PropertyMetadata(DateTime.Now, OnDateTimeChanged));    private static void OnDateTimeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)    {        DateTimeSelector2 target = obj as DateTimeSelector2;        DateTime oldValue = (DateTime)args.OldValue;        DateTime newValue = (DateTime)args.NewValue;        if (oldValue != newValue)            target.OnDateTimeChanged(oldValue, newValue);    }    public DateTimeSelector2()    {        this.DefaultStyleKey = typeof(DateTimeSelector2);    }    ///     /// 获取或设置Date的值    ///       public DateTime Date    {        get { return (DateTime)GetValue(DateProperty); }        set { SetValue(DateProperty, value); }    }    ///     /// 获取或设置Time的值    ///       public TimeSpan Time    {        get { return (TimeSpan)GetValue(TimeProperty); }        set { SetValue(TimeProperty, value); }    }    ///     /// 获取或设置DateTime的值    ///       public DateTime DateTime    {        get { return (DateTime)GetValue(DateTimeProperty); }        set { SetValue(DateTimeProperty, value); }    }    private bool _isUpdatingDateTime;    protected virtual void OnDateChanged(DateTime oldValue, DateTime newValue)    {        UpdateDateTime();    }    protected virtual void OnTimeChanged(TimeSpan oldValue, TimeSpan newValue)    {        UpdateDateTime();    }    protected virtual void OnDateTimeChanged(DateTime oldValue, DateTime newValue)    {        _isUpdatingDateTime = true;        try        {                Date = newValue.Date;                Time = newValue.TimeOfDay;        }        finally        {            _isUpdatingDateTime = false;        }    }    private void UpdateDateTime()    {        if (_isUpdatingDateTime)            return;            DateTime = Date.Date.Add(Time);    }}

控件的代码并不清楚ControlTemplate中包含什么控件,它只关心自己的数据。

XAML中通过绑定使用这些数据。

38937-20170318170806385-1363245943.png

这里给出了两个Style,分别使用了CalendarDatePicker 和DatePicker ,通过TwoWay Binding访问DateTimeSelector2中的Date属性。如果你的TemplatedControl需要有良好的扩展能力,可以尝试使用这种方式。

转载地址:http://rpvyx.baihongyu.com/

你可能感兴趣的文章
湘潭邀请赛——Alice and Bob
查看>>
js设置定时器
查看>>
数据库除运算
查看>>
LeetCode--112--路径总和
查看>>
DeviceIOControl与驱动层 - 缓冲区模式
查看>>
感悟贴2016-05-13
查看>>
vim使用教程
查看>>
JDK在LINUX系统平台下的部署案例与总结
查看>>
跨vlan通信-----单臂路由技术
查看>>
百度编辑器ueditor 光标位置的坐标
查看>>
DEV-C++ 调试方法简明图文教程(转)
查看>>
VS2017+EF+Mysql生成实体数据模型(解决闪退的坑)
查看>>
C++多态、继承的简单分析
查看>>
库克称未来苹果用户可自己决定是否降频 网友:你是在搞笑吗?
查看>>
6倍性能差100TB容量,阿里云POLARDB咋实现?
查看>>
linux 安装 MySQLdb for python
查看>>
Sublime Text 2 技巧
查看>>
使用fscanf()函数从磁盘文件读取格式化数据
查看>>
网站一些error_log报错
查看>>
参加婚礼
查看>>