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

知识回顾

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

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

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

内容概要

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

一.属性值验证

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

public static readonly DependencyProperty AgeProperty =
DependencyProperty.Register("Age", typeof(int), typeof(DPCustomPeople),
new PropertyMetadata(1), new ValidateValueCallback(AgeValidateValueCallback)); public virtual int Age
{
get { return (int)GetValue(AgeProperty); }
set { SetValue(AgeProperty, value); }
} public static bool AgeValidateValueCallback(object value)
{
int age=(int)value;
if (age < 1) return false;
return true;
}

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

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

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

二.默认值问题

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

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

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

public class RootClass
{
public RootClass()
{
this.Name = "RootClass";
}
public string Name { get; set; }
} public class SubClass : RootClass
{
public SubClass()
{
this.Name = "SubClass";
}
}

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

public class RootClass
{
public RootClass()
{
Name = "RootClass";
}
public static string Name { get; set; }
} public class SubClass : RootClass
{
public SubClass()
{
Name = "SubClass";
}
}

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

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

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

上篇有提到过这样一句话

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

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

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

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

四.属性元数据的唯一性

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

public class DPCategory : DependencyObject
{
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DPCategory),
new PropertyMetadata(string.Empty)); public void TestOverrideMetadata()
{
//wrong
NameProperty.OverrideMetadata(typeof(DPCategory), new PropertyMetadata("WPF"));
}
}

将会抛出异常

五.重写属性元数据

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

public class DPWPFCategory : DPCategory
{
public void TestOverrideMetadata()
{
NameProperty.OverrideMetadata(typeof(DPWPFCategory), new PropertyMetadata("WPF"));
Console.WriteLine(NameProperty.GetMetadata(typeof(DPWPFCategory)).DefaultValue);
//output 30
}
}

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

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

public string Group
{
get { return (string)GetValue(GroupProperty); }
set { SetValue(GroupProperty, value); }
}
public static readonly DependencyProperty GroupProperty =
DependencyProperty.Register("Group", typeof(string), typeof(DPCategory),
new PropertyMetadata(string.Empty,new PropertyChangedCallback(GroupPropertyChangedCallback))); public static void GroupPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine(e.NewValue);
}

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

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

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

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

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

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

如下示例

public int Order
{
get { return (int)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
} public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register("Order", typeof(int), typeof(DPCategory), new UIPropertyMetadata(0));
//UIPropertyMetadata

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

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

输出

六.改写属性元数据

改写与重写有几点不同.

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

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

public class DPGroup:DependencyObject
{
public static readonly DependencyProperty GroupProperty =
DPCategory.GroupProperty.AddOwner(typeof(DPGroup),
new PropertyMetadata("Technology"));
public string Group
{
get { return (string)GetValue(GroupProperty); }
set { SetValue(GroupProperty, value); }
} public void TestAddOwnerMetadata()
{
Console.WriteLine(Group);
this.Group = "Computer Technology";
}
}

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

本篇主要讲到了属性元数据的一些特点及注意点.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. TCP的滑动窗口与拥塞窗口

    一.滑动窗口 滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的.   对ACK的再认识,ack ...

  2. opencv cvtColor dtype issue(error: (-215) )

    opencv cvtColor dtype issue(error: (-215) ) 更详细的错误信息如下,color.cpp:9710: error: (-215) depth == CV_8U ...

  3. mysql8 mongodb4 增删改查 性能对比,2019 最专业对比,nosql 真的比 sql 性能强很多?

    原文:mysql8 mongodb4 增删改查 性能对比,2019 最专业对比,nosql 真的比 sql 性能强很多? 版权所有:http://www.fengyunxiao.cn 近几年看了很多关 ...

  4. [RxJS] Implement pause and resume feature correctly through RxJS

    Eventually you will feel the need for pausing the observation of an Observable and resuming it later ...

  5. hdu 4406 费用流

    这题问题就是当前时刻究竟选择哪门课程,易知选择是和分数有关的,而且是一个变化的权值,所以能够用拆点的方式,把从基础分到100分都拆成点.但若这样拆点的话,跑费用流时就必须保证顺序.这样就麻烦了..观察 ...

  6. 正确理解Spring事务和数据库事务和锁

    Lock wait timeout exceeded; try restarting transaction解决方案 参考文章 Spring中@Transactional事务回滚 http://www ...

  7. Linux系统编程_8_进程控制之fork_wait_waitpid函数

    fork函数: #include <unistd.h>        pid_t fork(void); fork用来创建一个子进程: 特点: fork调用后会返回两次,子进程返回0,父进 ...

  8. ASCII,Unicode和UTF-8终于找到一个能完全搞清楚的文章了

    前言 平时喜欢写东西,看博客,一直对编码有些懵,今天下午也不知道看到了什么,突然想了解下,就找到了这个文章,看完真的豁然开朗,这个必须留下来做纪念. 点击打开链接 1.ASCII 我们知道,计算机内部 ...

  9. js进阶正则表达式6转义字符(加\转义)(.符号)(|符号)

    js进阶正则表达式6转义字符(加\转义)(.符号)(|符号) 一.总结 转义字符:{} () / $ # & * . ....... //3.特殊字符,都要加转义\ 点符号:var reg2= ...

  10. MySQL参数文件位置

    对于linux/unix: mysql --help|grep my.cnf   /etc/my.cnf, /etc/mysql/my.cnf, /usr/local/etc/my.cnf, ~/.m ...