[笔记]WPF中的新特性——依赖属性(6)

来源:互联网 发布:mysql 查询语句count 编辑:程序博客网 时间:2024/05/21 19:21

至此,有关依赖属性的话题就告一段落了,让我们回顾一下有关依赖属性的一些知识。下面提到的许多例子都可以在前面的文章中找到,这里就不在赘述了。

 

图:与ProgessBar的依赖属性Value相关一些过程、类型、关系

 

上图画的有点乱,不过我们还是可以从其中看出一些门道:

1)依赖属性(DependencyProperty)值需要使用依赖对象(DependencyObject)的GetValueSetValue来读/写;

2WPF中有众多类派生自DependencyObject,因此它们都会继承GetValueSetValue方法;

3)从DependencyObject派生的类可以定义习惯上是public static readonlyDependencyProperty对象,并且在其静态构造方法中使用DependencyProperty.Register方法向属性系统注册依赖属性;有些依赖属性对于C#代码来说是多于的,因为总可以使用别的方式来获得同样的属性值,但是它们对于XAML来说却是完全必要的;

4)依赖属性总是伴随一些元数据,这些元数据是该属性在属性系统内的特征。属性系统内部会有某些机制来维护它们,但我们也可以通过某些方法将对应的元数据传入,如FrameworkPropertyMetadata

5)当依赖属性值发生变化的时候,通常会调用FrameworkPropertyMetadata.PropertyChangedCallback来处理,处理过程引发相应的事件处理器,程序代码可以通过实现事件处理器来处理变化;或是改变某些依赖属性的值,使得XAML可以利用这些它们来实现特殊的效果(如IsMouseOver的触发器);

6)有些依赖属性的值可以被子元素继承,通常会给依赖属性设置指定了FrameworkPropertyMetadataOptions.Inherits的元数据(如看到Inherits标志,就使用LogicalTreeHelper遍历当前元素节点及其子结点,依次将子元素对象的对应依赖属性设置为与当前元素对象的相同值);

7)为了确定依赖属性的值,共有8种优先级不同的提供器,属性值继承提供器只是其中一种,因此某些元素的属性没有获得继承值的原因,通常是被比属性值继承提供器更优先的提供器设定过了。依赖属性值的确定过程很大程度上依靠提供器的优先顺序,需要格外注意,否则会出现明明设置了值,却不按预想显示的问题(如之前的StatusBar)。整个这个过程共分5步:获得基本值、计算表达式、处理动画、强制和验证。

8)附加属性有两种用法,

i伪继承

某个类(通常从DependencyObject派生,如TextElement)定义了一个依赖属性,并且通过RegisterAttached方法进行了注册;其它类(如Control)也定义了一个依赖属性(通常是同名的),并且使用AddOwner方法为其添加了所有者。这样就仿佛Control是从TextElement派生而来的一样,因为它也包含了TextElement中的依赖属性,并且这个依赖属性一旦被“伪基类”TextElement改变,“伪子类”Control中的副本也会跟着变。不过既然称作“伪”,那么实际上ControlTextElement就没有继承关系,而仅是通过附加属性联系在一起。

这种用法很有意义,如上图所示,TextElement定义的附加属性在Control中都有与之相对的,且在Control的静态构造方法中使用AddOwner获得了它的引用。从Control派生的所有子类中都继承了由Control定义的那个对应的依赖属性。如前例,在StackPanel上设置TextElement.FontSize会导致其值被改变。由于StackPanel的所有子元素继承这个属性值,因此ControlFontSize的副本会跟着改变,继而改变Control子类Button等的字体大小的呈现。

ii直接附加

由于DependencyObjectSetValueGetValue都需要DependencyProperty类型的参数,因此可以直接在某DependencyObject的对象身上调用这两个方法,并传入任何DependencyProperty的对象。

这种用法就是为了给DependencyObject的实例附加任意的依赖属性,只是如果附加的属性对实例来说有意义,那就有意义(如之前文章中给GeomertyModel3D的实例附加FrameworkElement.TagProperty属性),否则就是没意义。

9)书里提到过依赖属性具有高效的存储结构,不过至于这是种怎样的结构,就不得而知了。尽管可以利用反射器一点点分析出来,不过我想似乎也没有这样的必要。

【以下内容全部都是我的猜想,没有经过验证!】

经过试验,我发现有三种情况都会使得DependencyProperty将对应的依赖属性存入那个结构,权且假设它就是个哈希表吧。这三种情况是:

i)使用DependencyProperty.RegisterRegisterAttached等方法注册依赖属性;

ii)使用DependencyProperty.AddOwner方法添加所有者;

iii)使用DepenedencyObject.SetValue设定依赖属性值。

第一种显而易见,注册属性的时候需要指定所有者,这样就可以建立如下关系:

              Key               Value

              所有者           注册的依赖属性

第二种仔细想想也容易看出来:

              Key                             Value

              注册时的所有者           注册的依赖属性           (这是通过Register完成的映射)

              添加的所有者              同上                            (这是由AddOwner添加的映射)

当依赖属性发生变化时,属性系统会根据这些映射给每个所有者发送变更通知,如前所述。

第三种需要做个简单的试验,通过定义两个不同的DependencyObject来获得:

 

GeometryModel3D model1 = new GeometryModel3D(),

model2 = new GeometryModel3D();

model1.SetValue(FrameworkElement.TagProperty, "model1");

model2.SetValue(FrameworkElement.TagProperty, "model2");

Debug.WirteLine(model.GetValue(FrameworkElement.TagProperty) as String);

Debug.WirteLine(model.GetValue(FrameworkElement.TagProperty) as String);

 

    控制台分别输出字符串model1model2。由此可以看出来,那个结构存储了两个对象与同一个属性的映射,比如

    Key        Value

    model1     FrameworkElement.TagProperty的副本1

    model2     FrameworkElement.TagProperty的副本2

    这样只要在model1上调用GetValue,那么必然获得附加在model1身上的依赖属性值,并且不会因其与model2附加的是相同的属性,就会获得model2的属性值。

 

一言以蔽之,WPF中的依赖属性非常多,它们在对XAML来说至关重要。