原文:wpf控件开发基础(4) -属性系统(3)

知识回顾

接上篇,上篇我们真正接触到了依赖属性的用法,以及依赖属性的属性元数据的用法,并且也实实在在地解决了之前第二篇提到的一系列问题.来回顾一下

  1. 属性默认值
  2. 属性变更通知
  3. 属性强制回调

本篇将继续讨论上一篇提到的问题,主题依然是属性元数据.

内容概要

  1. 属性值验证
  2. 默认值问题
  3. 依赖属性的不变与可变
  4. 属性元数据的唯一性
  5. 重写属性元数据
  6. 改写属性元数据

一.属性值验证

依赖属性具备属性验证的功能,其也会对依赖属性默认值进行验证,在DependencyProperty的Register方法中最后一个参数为ValidateValueCallback,用于属性值验证,如下代码

  1. public static readonly DependencyProperty AgeProperty =
  2. DependencyProperty.Register("Age", typeof(int), typeof(DPCustomPeople),
  3. new PropertyMetadata(1), new ValidateValueCallback(AgeValidateValueCallback));
  4.  
  5. public virtual int Age
  6. {
  7. get { return (int)GetValue(AgeProperty); }
  8. set { SetValue(AgeProperty, value); }
  9. }
  10.  
  11. public static bool AgeValidateValueCallback(object value)
  12. {
  13. int age=(int)value;
  14. if (age < 1) return false;
  15. return true;
  16. }

ValidateValueCallback有一个传入的参数(即依赖属性值)和一个返回值(表示属性值是否是正确的).

注意:
1.当返回值为false的时候,将会抛出异常
2.不要试图在该回调方法中校正传入的值类型(即使引用类型也不符合逻辑),其主要职责在于验证.

此功能用的不是很频繁,因为返回false会抛异常,还要额外处理.再说强制回调方法也可以处理.

二.默认值问题

上一篇有讲到依赖属性的默认值问题

在构造函数里给属性赋值并不能解决问题(这是依赖属性默认值带来的额外问题,因为其是静态属性)

类继承带来的默认值重写问题,看以下代

  1. public class RootClass
  2. {
  3. public RootClass()
  4. {
  5. this.Name = "RootClass";
  6. }
  7. public string Name { get; set; }
  8. }
  9.  
  10. public class SubClass : RootClass
  11. {
  12. public SubClass()
  13. {
  14. this.Name = "SubClass";
  15. }
  16. }

上面代码应该没问题,但如果这样就有问题了

  1. public class RootClass
  2. {
  3. public RootClass()
  4. {
  5. Name = "RootClass";
  6. }
  7. public static string Name { get; set; }
  8. }
  9.  
  10. public class SubClass : RootClass
  11. {
  12. public SubClass()
  13. {
  14. Name = "SubClass";
  15. }
  16. }

如果是静态属性的话,子类一旦改写就会改变父类,破坏内部逻辑

那么就需要保证属性在每个类默认值的唯一性.

三.依赖属性的不变与可变

上篇有提到过这样一句话

(依赖属性)属性名字,属性类型,属性所有者类型一经注册将无法更改

这便不可变的地方,即使在子类重写也不行.
但依赖属性的默认值是可以修改的,修改的前提是保证父类的属性默认值不被影响,上篇我们有提出疑问,给默认值为什么需要属性元数据(PropertyMetadata),以下的代码也应该走的通

  1. public static readonly DependencyProperty NameProperty =
  2. DependencyProperty.Register("Name", typeof(string), typeof(DPCustomPeople),
  3. string.Empty);

是的,若依赖属性内部有一个列表维护默认值,也保证了在不同类型中默认值不会发生错误.这样的代码是没问题的,但不要忘了,还有属性变更通知和属性强制回调.PropertyMetadata把依赖属性可变的行为全部抽取出来了,我想这样做的原因可以保持依赖属性功能的清晰程度(个人猜想,但确实有这样的效果).那么就意味着我们可以重写属性元数据.

四.属性元数据的唯一性

DependencyProperty提供了OverrideMetadata方法重写属性元数据,但一个依赖属性在同一个类中不允许重写,即在同一类型中,保证属性元数据在依赖属性中的唯一性,如下代码是错误的

  1. public class DPCategory : DependencyObject
  2. {
  3. public static readonly DependencyProperty NameProperty =
  4. DependencyProperty.Register("Name", typeof(string), typeof(DPCategory),
  5. new PropertyMetadata(string.Empty));
  6.  
  7. public void TestOverrideMetadata()
  8. {
  9. //wrong
  10. NameProperty.OverrideMetadata(typeof(DPCategory), new PropertyMetadata("WPF"));
  11. }
  12. }

将会抛出异常

五.重写属性元数据

继承的子类允许重写属性元数据,如下代码

  1. public class DPWPFCategory : DPCategory
  2. {
  3. public void TestOverrideMetadata()
  4. {
  5. NameProperty.OverrideMetadata(typeof(DPWPFCategory), new PropertyMetadata("WPF"));
  6. Console.WriteLine(NameProperty.GetMetadata(typeof(DPWPFCategory)).DefaultValue);
  7. //output 30
  8. }
  9. }

注意:在重写属性元数据时,应该要知道一下规则.
1.重写元数据是一个以重写元数据为主的合并的过程,即子类的重写后的元数据会与父类的进行合并

我们在父类定义一个Group的依赖属性

  1. public string Group
  2. {
  3. get { return (string)GetValue(GroupProperty); }
  4. set { SetValue(GroupProperty, value); }
  5. }
  6. public static readonly DependencyProperty GroupProperty =
  7. DependencyProperty.Register("Group", typeof(string), typeof(DPCategory),
  8. new PropertyMetadata(string.Empty,new PropertyChangedCallback(GroupPropertyChangedCallback)));
  9.  
  10. public static void GroupPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  11. {
  12. Console.WriteLine(e.NewValue);
  13. }

然后在子类重写此依赖属性的元数据

  1. public void TestOverrideMergeMetadata()
  2. {
  3. GroupProperty.OverrideMetadata(typeof(DPWPFCategory), new PropertyMetadata("Computer Technology"));
  4. Console.WriteLine(this.Group);
  5. this.Group = "Technology";
  6. }

输出结果,当属性值发生变化时,父类的属性变更通知回调方法就会触发

2.实例匹配(Type.IsAssignableFrom 返回为true)

注意:要重写的父类属性元数据类型必须要是重写的属性元数据的父类或者与之类型相同.如下为属性元数据的继承关系

可以看出如果用FrameworkPropertyMetadata来重写的话是最保险的

如下示例

  1. public int Order
  2. {
  3. get { return (int)GetValue(OrderProperty); }
  4. set { SetValue(OrderProperty, value); }
  5. }
  6.  
  7. public static readonly DependencyProperty OrderProperty =
  8. DependencyProperty.Register("Order", typeof(int), typeof(DPCategory), new UIPropertyMetadata(0));
  9. //UIPropertyMetadata

注意父类的属性元数据为UIPropertyMetadata,以下子类重写的属性元数据类型为PropertyMetadata 即会报错.

  1. public void TestOverrideDriveMetadata()
  2. {
  3. OrderProperty.OverrideMetadata(typeof(DPWPFCategory), new PropertyMetadata(1));
  4. //wrong
  5. //must >=UIPropertyMetadata
  6. }

输出

六.改写属性元数据

改写与重写有几点不同.

  1. 改写是将已存在的依赖属性添加到不同类型当中
  2. 改写不是一个合并属性元数据的过程,改写后的是全新的

如下:
为一个全新的类型添加已有依赖属性,并重写属性元数据,重写后将不会触发DPCategory类型的GroupProperty属性变更通知,不会影响原依赖属性逻辑

  1. public class DPGroup:DependencyObject
  2. {
  3. public static readonly DependencyProperty GroupProperty =
  4. DPCategory.GroupProperty.AddOwner(typeof(DPGroup),
  5. new PropertyMetadata("Technology"));
  6. public string Group
  7. {
  8. get { return (string)GetValue(GroupProperty); }
  9. set { SetValue(GroupProperty, value); }
  10. }
  11.  
  12. public void TestAddOwnerMetadata()
  13. {
  14. Console.WriteLine(Group);
  15. this.Group = "Computer Technology";
  16. }
  17. }

注意:如果在子类进行此操作,将与重写效果一样

本篇主要讲到了属性元数据的一些特点及注意点.FrameworkPropertyMetadata还有许多特性,但必须有实际场景结合.所以这里无法展开.讲了这么多貌似都没触及到WPF什么事,这部分也是比较枯燥,但却非常重要.下篇继续,然后依赖属性就告一段落.

Demo下载

wpf控件开发基础(4) -属性系统(3)的更多相关文章

  1. wpf控件开发基础(3) -属性系统(2)

    原文:wpf控件开发基础(3) -属性系统(2) 上篇说明了属性存在的一系列问题. 属性默认值,可以保证属性的有效性. 属性验证有效性,可以对输入的属性进行校验 属性强制回调, 即不管属性有无发生变化 ...

  2. wpf控件开发基础(2) -属性系统(1)

    原文:wpf控件开发基础(2) -属性系统(1) 距离上篇写的时间有1年多了.wpf太大,写的东西实在太多,我将依然围绕着自定义控件来展开与其相关的技术点. 也欢迎大家参与讨论.这篇我们将要讨论的是W ...

  3. wpf控件开发基础

    wpf控件开发基础(3) -属性系统(2) http://www.cnblogs.com/Clingingboy/archive/2010/02/01/1661370.html 这个有必要看看 wpf ...

  4. wpf控件开发基础(5) -依赖属性实践

    原文:wpf控件开发基础(5) -依赖属性实践 知识回顾 接上篇,回顾这三篇讲了什么东西 首先说明了属性的现存问题,然后介绍了依赖属性的基本用法及其解决方案,由于依赖属性以静态属性的方式存在,进而又介 ...

  5. asp.net控件开发基础(1)(转)原文更多内容

    asp.net本身提供了很多控件,提供给我们这些比较懒惰的人使用,我认为控件的作用就在此,因为我们不想重复工作,所以要创建它,这个本身便是一个需求的关系,所以学习控件开发很有意思. wrox网站上有本 ...

  6. WPF控件开发(2) 自动完成(AutoComplete)-1

    自动完成功能使用范围很广,多以TextBox或ComboBox的形式出现,在输入的同时给予候选词,候选词一般有两种方式获取. 一种类似Baidu,Google,Bing之类的搜索引擎所用的直接给予前十 ...

  7. 跟我一起学WPF(2):WPF控件基础

    WPF控件简介 通过上一篇XAML语言的介绍,我们知道,XAML是一个树形结构,同样,WPF控件作为构成整个XAML树的一部分,也是一个树形结构.我们看一个简单的例子. <Button.Cont ...

  8. 浅谈Winform控件开发(一):使用GDI+美化基础窗口

    写在前面: 本系列随笔将作为我对于winform控件开发的心得总结,方便对一些读者在GDI+.winform等技术方面进行一个入门级的讲解,抛砖引玉. 别问为什么不用WPF,为什么不用QT.问就是懒, ...

  9. C# Winform开发以及控件开发的需要注意的,被人问怕了,都是基础常识

    我是搞控件开发的,经常被人问,所以把一些问题记录了下来!如果有人再问,直接把地址丢给他看. 一. 经常会有人抱怨Winform界面闪烁,下面有几个方法可以尽可能的避免出现闪烁 1.控件的使用尽量以纯色 ...

随机推荐

  1. 【Eclipse提高开发速度-插件篇】Checkstyle的使用

    1.CheckStyle是SourceForge下的一个项目,提供了一个帮助JAVA开发者遵守某些编码规范的工具. CheckStyle提供了大部分功能都是对于代码规范的检查 CheckStyle检验 ...

  2. 全双工 串口 stm32

  3. CSU1656: Paper of FlyBrother(后缀数组)

    Description FlyBrother is a superman, therefore he is always busy saving the world.  To graduate fro ...

  4. 关于db2的一点记录

    近期听搞db2的兄弟说:db2数据库软件的license 不区分平台(os). 先记下来.像db2这么高大上的软件,接触的机会是比較少的. 另外:db2 的license是须要打的,不打的话,超过一段 ...

  5. swift开发网络篇—利用NSURLConnection GET请求和POST请求

    一.GET请求和POST请求简单说明 @IBOutlet weakvar userName:UITextField! @IBOutletweakvar userPwd:UITextField! @IB ...

  6. php课程 8-29 gd库能够画哪些东西

    php课程 8-29 gd库能够画哪些东西 一.总结 一句话总结:文字,点,线,圆,弧线,矩形,各种形状都是可以的,和html5中的canva能画的东西很像,使用也很像,参数怎么记呢,参数完全不用记, ...

  7. 安装使用jupyter(原来的notebook)

    1.安装pyzmq 使用pip install pyzmq,安装不成功. 使用easy_install.exe pyzmq.成功安装. 2.安装tornado pip tornado 安装完尚不成功. ...

  8. xmpp即时通讯协议的特性---长处和缺点!

    xmpp协议的定义? XMPP是一种基于标准通用标记语言的子集XML的协议,它继承了在XML环境中灵活的发展性. 因此.基于XMPP的应用具有超强的可扩展性.经过扩展以后的XMPP能够通过发送扩展的信 ...

  9. [Linux] Search the contents of files using grep

    Learn the basic syntax for using grep to search the contents of a single file or files. It's like CM ...

  10. [Git] Use git add --patch for better commit history and mitigating bugs

    Let's split our changes into separate commits. We'll be able to check over our changes before stagin ...