天天看点

依赖属性之“风云再起”四十. PropertyMetadata测试代码十一. PropertyMetadata实现代码

前面我们看到一个依赖属性的注册最全的形式是下面这样子的:

  第一个参数是该依赖属性的名字,第二个参数是依赖属性的类型,第三个参数是该依赖属性的所有者的类型,第五个参数就是一个验证值的回调委托,那么最使我们感兴趣的还是这个可爱的 PropertyMetadata ,也就是我们接下来要讲的元数据。 提到WPF属性元数据,大家可能第一想到的是刚才的PropertyMetadata,那么这个类到底是怎样的呢?我们应该怎样使用它呢?首先我们看它的构造函数(我们选参数最多的来讲):

  其中的第一个参数是默认值,最后两个分别是PropertyChanged(变化通知)以及Coerce(强制)的两个委托变量,我们在实例化的时候,只需要把这两个委托变量关联到具体的方法上即可。

  事实上,除了PropertyMetadata以外,常见的还有 FrameworkPropertyMetadata,UIPropertyMetadata。他们的继承关系是F->U->P。其中以 FrameworkPropertyMetadata参数最多,亦最为复杂。

FrameworkPropertyMetadata的构造函数提供了很多重载,我们挑选最为复杂的重载来看它到底有哪些参数以及提供了哪些功能:

  其中第一个参数是默认值,最后两个参数分别是是否允许动画,以及绑定时更新的策略(在Binding当中相信大家并不陌生),这个不详细解释 了。重点看一下里第三、四两个参数,两个 CallBack的委托。结合前面Register的时候提到的ValidateValueCallback共组成三大”金刚“,这三个Callback 分别代表Validate(验证),PropertyChanged(变化通知)以及Coerce(强制)。当然,作为 Metadata,FrameworkPropertyMetadata只是储存了该依赖属性的策略信息,WPF属性系统会根据这些信息来提供功能并在适 当的时机回调传入的delegate,所以最重要的还是我们定义的这些方法,通过他们传入委托才能起到真正的作用。

具体PropertyMetadata包含哪些成员呢?我们先看微软的PropertyMetadata类

<a href="http://images.cnblogs.com/cnblogs_com/KnightsWarrior/WindowsLiveWriter/d1051bd1bd80_136FD/2010-8-26%2019-14-21_2.png"></a>

  在写其他测试用例之前,我们先来创建两个类,第一个类TestDepObj,内部注册了四个依赖属性,前三个没有元数据操作,也就是没有显示声 明并构造元数据类,第四个添加了一个元数据类,这个元数据类包含了默认值、值改变回调委托、强制值回调委托。第二个类TestSubclass继承自 TestDepObj。

  大家看到我们在创建PropertyMetadata的时候对某些功能并没有实现,这里我们就通过子类来具体实现,MONO的这种做法想沿袭微 软PropertyMetadata、FrameworkPropertyMetadata和UIPropertyMetadata的做法,但是个人觉得 它实现得并不是太好,很多地方感觉很别扭。

  下面的测试代码主要看一下元数据的默认值,实例化一个元数据类,然后调用它的DefaultValue、PropertyChangedCallback、CoerceValueCallback,测试他们是否为Null。

  我们在WPF和Silverlight中都有过这样的体会:到底什么时候这个依赖属性不能再修改了,其实这个操作得归功于OnApply什么时 候触发,我们也可以调用IsSealed来查看,那么这里我们就先写测试代码。第一段代码直接显示调用CallApply方法进行密封;第二段代码则是通 过OverrideMetadata操作后内部调用的CallApply;第三段代码是通过AddOwner操作中调用的CallApply;最后一段代 码通过调用DependencyProperty.Register时传入元数据,在其内部调用CallApply。

下面这段测试代码是验证AddOwner后的DependencyProperty是否和原来的DependencyProperty是同一个DependencyProperty。

  下面这个测试用例是首先实例化元数据并作为注册依赖属性时的参数传入,大家都知道此时如果想修改元数据,可以通过AddOwner或者OverrideMetadata,如果直接赋值,会抛出错误,因为元数据已经密封。

这个和上面的那个测试用例基本一样,只不过把CoerceValueCallback换成了PropertyChangedCallback

下面这个测试用例也和上面的两个测试用例类似,它是修改元数据的DefaultValue

  通过前面的测试用例,大家可能都会发现有一个Merge这个方法,它在什么时候调用呢?其实它在OverrideMetadata和AddOwner操作中都会调用,在

DependencyProperty中的Register也会显示调用一次。我们需要注意的是:在元数据密封了以后就会抛出错误。

下面的测试用例主要是默认值是不能被设置成Unset的

通过前面的多个测试用例,其实已经包含了PropertyMetadata的基本功能,那我们接下来就看一下PropertyMetadata的内部设计和实现。

MONO的PropertyMetadata类要比微软的PropertyMetadata类简单很多,不过我们也需要注意一下几点:

1,元数据类包含哪些成员以及有几个构造函数重载?因为这些直接关系到外部的调用。

2,大家要注意ValidateValueCallback不是PropertyMetadata的成员,所以在PropertyMetadata的构造函数中不要把它作为参数传入。

3,注意OnApply函数,因为调用它之后就不能修改元数据的成员,只有通过OverrideMetadata和AddOwner间接实现,如果大家想知道到底这个元数据有没有被密封,可以调用IsSealed属性来查看,这个功能我们也会经常用到。

4,元数据类中提供了Merge的功能,用来方便合并父类和子类的元数据。

在上面几个类就是依赖属性系统的核心类,下面将看到几个Helper类。

本文转自KnightsWarrior51CTO博客,原文链接: http://blog.51cto.com/knightswarrior/405233,如需转载请自行联系原作者