依赖关系属性和通知
|
依赖关系属性和通知 http://www.21tx.com 2009年02月09日 msdn
目录 依赖关系属性基础知识 绑定源与目标 使用通知自定义集合 依赖关系属性和事件 动态绑定技术 Freezable 差异 使用 DependencyPropertyDescriptor DependencyObjects 集合 这些天来,对象似乎已经忙得晕头转向了。每个人都希望它们做这做那。Windows Presentation Foundation (WPF) 应用程序中的典型对象会接到各种各样不同的请求:有要求绑定到数据的、有要求更改样式的、有要求从可见父项继承的,甚至还有要求来点动画让大家高兴一下的。 对象怎么才能建立起边界和优先级呢?WPF 的回答是一种称为依赖关系属性的功能。通过为 WPF 类提供结构化方法来响应由数据绑定、样式、继承和其他来源更改带来的变化,依赖关系属性已变得十分重要,其程度不亚于事件和事件处理对早期.net 程序员的重要性。 当然,依赖关系属性不是万能的,它可能无法为某些传统任务提供您所需要的所有功能。假设您可以访问具有依赖关系属性的对象,并且希望在其中某个属性发生变化时得到通知。这对于事件处理程序来说好像并不是什么难事——但您要知道实际上根本不存在这样的事件! 当您使用对象集合时,这个问题更加突出。在某些情况下,您可能希望当集合中对象的某些特定依赖关系属性发生更改时得到通知。现有唯一的解决方案是 FreezableCollection<T>,它能够告诉您何时发生变化——但不能告诉您是什么发生了变化。 换句话说,依赖关系属性并不总能与其他各方良好协作。本专栏主要讲述的就是如何弥补它们在通知方面的不足。 依赖关系属性基础知识 假设您正在设计名为 PopArt 的 WPF 类,并且希望定义类型为 Brush 的属性 SwirlyBrush。如果 PopArt 继承自 DependencyObject,您就可以将 SwirlyBrush 定义为 DependencyProperty。第一步是公共静态只读字段:
依赖关系属性与该属性具有相同的名称,但附加了 "Property" 字样。它是字段声明或静态构造函数的一部分,您可以随后注册依赖关系属性:
您还需要能够提供对该属性进行正常访问的传统属性定义(有时称为 CLR 属性):
SetValue 和 GetValue 方法由 DependencyObject 定义,这就是所有定义依赖关系属性的类必需从该类派生的原因。除调用这两种方法外,CLR 属性不应当包含任何其他代码。CLR 属性通常被认为是由依赖关系属性支持的。 依赖关系属性注册中引用的 OnSwirlyBrushChanged 方法是一种回调方法,任何时候只要 SwirlyBrush 属性发生变化便会调用该方法。因为该方法与静态字段关联,所以它自身也必须是静态方法:
第一个参数是属性发生改变的类的特定实例。如果已经在名为 PopArt 的类中定义过此依赖关系属性,则第一个参数始终是类型为 PopArt 的对象。我喜欢使用与静态方法相同的名称定义实例方法。静态方法随后按照如下方式调用实例方法:
该实例方法中包含用于应对 SwirlyBrush 值更改所需的所有项目。 在代码中,您可以采用正常方式在 PopArt 的实例中设置 SwirlyBrush 属性的值:
但是,还需要注意 PopArt 类中存在名为 PopArt.SwirlyBrushProperty 的公共静态字段。该字段是类型为 DependencyProperty 的有效对象,独立于所有类型为 PopArt 的对象存在。这使您能够在创建具有该属性的对象之前引用类的特定属性。 由 DependencyObject 定义的 SetValue 方法也是公共的,因此您可以采用如下方式设置 SwirlyBrush 属性:
此外,如果具有如图 1 所示的三个对象,您可以使用以下这段完全通用的代码设置属性:
图 1 对象 如果值类型与 DependencyProperty 关联的类型(本例中为 Brush)不一致,则会抛出异常。但 DependencyProperty 定义的 IsValidType 方法可以帮助避免此类问题。 使用 DependencyProperty 对象与 SetValue 和 GetValue 方法引用和设置属性的过程,比早期通过 "SwirlyBrush" 等字符串引用属性更为简单明了。采用这种方法指定的属性需要反射来实际设置属性。 绑定源和目标 依赖关系属性在 WPF 中的广泛使用在设置 XAML 形式的数据绑定、样式和动画时并不明显,但当您在代码中完成这些任务时却是显而易见的。BindingOperations 和FrameworkElement 定义的 SetBinding 方法需要类型为 DependencyProperty 的对象作为绑定目标。WPF 样式中使用的 Setter 类需要 DependencyProperty 对象。BeginAnimation 方法还需要使用 DependencyProperty 对象作为动画目标。 这些目标必须都是 DependencyProperty 对象才能使 WPF 施加合适的优先权规则。例如,动画设置的属性值比样式设置的属性值优先权高。(如果需要了解有哪些源负责特定的属性值,您可以使用 DependencyPropertyHelper 类。) 尽管数据绑定目标必须是依赖关系属性,但绑定源可以不必是依赖关系属性。很显然,如果绑定需要能够成功监控源中的变化,则绑定源必须采纳某种通知机制,而 WPF 实际上允许三种不同类型的源通知。并且这几种类型是互斥的。 第一种(同时也是首选)方法是对源和目标均使用依赖关系属性。如相同的属性在不同上下文中分别担当绑定源和目标两种角色,那么这是一种不错的解决方案。 第二种方法包括根据属性名称定义事件。举例来说,如果某个类定义了名为 Flavor 的属性,则它还将定义名为 FlavorChanged 的事件。顾名思义,该类将在 Flavor 的值发生变化时触发此事件。这种类型的通知在Windows 窗体中广泛使用(例如,由 Control 定义的 Enabled 属性具有相应的 EnabledChanged 事件)。但应该避免在 WPF 代码中使用这种方法,原因在此不再赘述。 最后一种可行的方法是实现 INotifyPropertyChanged 接口,它要求类根据 PropertyChangedEventHandler 委托定义名为 PropertyChanged 的事件。相关联的 PropertyChangedEventArgs 对象具有名为 PropertyName 的属性,它代表发生更改的属性的名称字符串。 例如,如果类具有名为 Flavor 的属性和名为 flavor 的私有字段,则该属性的 set 访问器如下所示:
对 Flavor 属性变化感兴趣的类只需订阅 PropertyChanged 事件,即可在该属性(或该类中任何其他属性)发生变化时得到通知。 INotifyPropertyChanged 接口并不能做到万无一失。该接口所能控制的仅是名为 PropertyChanged 的事件。并且不能保证该类一定会触发此事件。在许多情况下,类会为某些公共属性触发该事件,但并不一定会为所有属性触发该事件。如果您无法访问源代码,那没有什么很好的方法能够事先了解哪些属性会触发 PropertyChanged,而哪些属性不会触发该事件。 无论如何,INotifyPropertyChanged 对于那些不会成为 WPF 数据绑定、样式或动画目标的属性来说仍然是一种优秀、简单的解决方案,但它必须提供对其他类的更改通知。 使用通知自定义集合 有时可能需要使用对象集合,并且需要在其中某个对象的属性发生变化时得到通知。最有价值的通知能够准确告诉您集合中哪一个项目发生了变化,并且同时还能指出该项中哪个属性发生了变化。 如集合中的所有对象都实现了 INotifyPropertyChanged 接口且集合本身实现了 InotifyCollectionChanged,这项任务相对就比较简单。当新项目添加到集合,或者删除现有项目时,实现该接口的集合会触发 CollectionChanged 事件。CollectionChanged 事件可以提供这些添加或删除项目的列表。或许泛型 ObservableCollection<T> 类是实现了 INotifyCollectionChanged 的最流行的集合对象。 让我们创建一个派生自 ObservableCollection<T> 的自定义集合类,并实现名为 ItemPropertyChanged 的新事件。集合中任何项目的属性发生变化均会触发该事件,并且伴随该事件的参数还包括项和发生变化的属性。图 2 显示派生自 PropertyChangedEventArgs 的 ItemPropertyChangedEventArgs 类,它包含类型为对象、名称为 Item 的新属性。图 2 还显示了 ItemPropertyChangedEventHandler 委托。 图 2 ItemPropertyChangedEventArgs
(编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
