企业网站建设

建站知识

今日已发布信息: 194276
累计注册用户: 82551184

WPF 依赖属性

普通属性 数据绑定 创建一个 其它功能

概述: 更多的时间是花在使用依赖属性而非创建它们的过程中,在许多情况下你仍需要创建自己的依赖属性。显然,当你设计一个自定义的WPF元素时,依赖属性是一个关键要素。此外,在某些情况下,如果你要给原本不支持数据绑定、动画或WPF的一些其它功能的一部分代码添加上述功能的时候,你需要创建依赖属性。创建一个依赖属性并不困难,但是要适应它的句法,还是需要花一点时间的。它与创建一个普通的.NET属性的工作完全不同。

每一个.NET程序员都熟悉属性和事件,这是.NET的对象抽象的核心部分。很少有人会想到WPF这样一个用户界面技术,会试图改变这两种基础要素。但令人惊讶的是,WPF就这样做了。


在本章中,您将学习WPF如何将普通.NET属性替换为更高级的依赖属性的过程。依赖属性使用更高效的存储,并支持更多的功能,如更改通知和属性值继承(元素树向下传播默认值的能力)。依赖属性也是WPF的一些关键功能的基础,包括动画,数据绑定,和样式等。幸运的是,尽管依赖属性的内部结构发生了变化,在程序中,你仍然可以用与传统的.NET属性完全相同的方式读取和设置依赖属性。接下来,您将会对依赖属性进行更详细的了解。您会看到他们是如何被定义、注册和使用的。您还将了解他们能够支持哪些功能,解决什么样的问题。


§提示  理解依赖属性需较强的理论知识,你现在可能还不希望涉及过多。如果你已经迫不及待地想开始构建一个应用程序,可考虑跳过此章节。当你希望对依赖属性有一个更深入的了解,并且想建立自己的依赖属性的时候,可以再返回到本章节内容。


了解依赖属性


依赖属性是.NET标准属性的一个新的执行,功能上具有一个显著的升级。要想操控WPF的一些核心功能,例如动画,数据绑定,和样式等,就离不开依赖属性。大多数由WPF元素公开的属性都是依赖属性。到目前为止你所看到的所有例子使用的都是依赖属性,只是你没有意识到这一点罢了。这是因为依赖属性在设计上具有和普通性属相同的使用方法。


然而,依赖属性不是普通性属。我们可以把依赖属性想象成添加了WPF功能的普通属性(以典型的.NET定义形式),这是一件令人欣慰的事情。从概念上讲,依赖属性拥有这样的表象行为,但是它们的幕后执行却不是这样的。原因很简单,就是要考虑到性能因素。如果WPF的设计者简单地在.NET属性系统之上添加额外的功能,他们需要为用户代码创建一个复杂,庞大的中介层,才能使用这些属性。没有这个额外的开销,普通属性是不能支持依赖属性的所有功能的。


依赖属性是WPF的独特创新。然而,WPF库中所有的依赖属性都是由.NET的普通属性过程包装起来的。这使得它们可以正常的方式使用,即使是WPF依赖属性系统无认知代码也可以实现对它们的使用。一个旧技术包装一个较新的技术,这看起来似乎很奇怪,但正是由于这种做法使得WPF在不破坏.NET世界其它功能的情况下,实现了对属性基本要素的改变。


定义一个依赖属性


尽管在编程过程中,更多的时间是花在使用依赖属性而非创建它们的过程中,在许多情况下你仍需要创建自己的依赖属性。显然,当你设计一个自定义的WPF元素时,依赖属性是一个关键要素。此外,在某些情况下,如果你要给原本不支持数据绑定、动画或WPF的一些其它功能的一部分代码添加上述功能的时候,你需要创建依赖属性。创建一个依赖属性并不困难,但是要适应它的句法,还是需要花一点时间的。它与创建一个普通的.NET属性的工作完全不同。


§提示  只能给依赖对象,即由DependencyObject派生的类,添加依赖属性。幸运的是,大多数WPF基础设施的关键件都是由DependencyObject间接派生而来的,最明显的例子就是前面章节中已经见到过的元素。


第一步是为你的属性定义一个代表它的对象。这是一个DependencyProperty类的实例。关于这个属性的所有信息必需是随时可用的,甚至可能需要在类之间共享的(这在WPF元素中是很常见的)。由于这个原因,你的DependencyProperty对象必须被定义为一个从属类DependencyObject中的静态字段。


例如,FrameworkElement类定义了一个由所有元素共享的Margin属性。毫无疑问,Margin是一个依赖属性。在FrameworkElement类中,它将是这样来定义的:

 

  酒泉教育网站  萧山纳思   今日推荐免费建站   分类信息   乌海网站建设公司

 


public class FrameworkElement:UIElement, ... {     public static readonly DependencyPropertyMarginProperty;     ... }


按照惯例,定义依赖属性的字段名称时,其命名是由普通属性名称以及在末尾添加Property的方法来完成的。这样一来,你可以把依赖属性的定义名称和实际属性的名称区别开。字段定义为只读,意味着它只能被FrameworkElement的静态构造函数来设置定义,这正是你接下来要承担的任务。


注册一个依赖属性


定义DependencyProperty对象仅仅是第一步。要想使用它,你还需要注册依赖属性。这个步骤需要在任何代码使用该属性之前完成,所以它必须在相关类的静态构造函数中完成。


WPF确保DependencyProperty对象不能被直接实例化,因为DependencyProperty类没有公共构造函数。相反,DependencyObject实例只能通过静态的DependencyProperty.Register()方法创建。WPF还确保了DependencyProperty对象创建之后不能被改变,因为所有的DependencyProperty成员都是只读的。因此,它们的赋值必须作为参数传递给Register()方法。


下面的代码显示了一个DependencyProperty属性如何创建的必要过程。在这里,FrameworkElement的类使用静态构造函数初始化MarginProperty:

static FrameworkElement() {     FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata     (new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure);       MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof  (FrameworkElement), metadata, new validateValueCallback(FrameworkElement.IsMarginValid));     ... }


注册依赖属性涉及如下两个步骤。首先,创建一个FrameworkPropertyMetadata对象,设定你的依赖属性(如支持数据绑定,动画和日志记录)要使用什么样的服务。接下来,通过调用静态DependencyProperty.Register()方法注册属性。在这一步骤中,你负责提供以下几个关键要素:


属性名(在本示例中为Margin)

该属性的数据类型(本例中为厚度结构)

拥有该属性的类(本例中为FrameworkElement类)

可选项,通过一个FrameworkPropertyMetadata对象为属性提供附加设置

可选项,对于属性执行验证的回调方法


前三个细节都比较简单。FrameworkPropertyMetadata对象和验证回调颇为有趣。您可以使用FrameworkPropertyMetadata为您的依赖属性配置其他功能。大多数FrameworkPropertyMetadata类的属性都是简单的布尔标志,您通过更改设置来翻转一个功能(每个布尔标志的默认值均为false)。有一些是指向你所创建的执行特定任务的自定义回调方法。其中FrameworkPropertyMetadata.DefaultValue用来设置属性首次初始化时的默认值。表4-1列出了所有的FrameworkPropertyMetadata属性。


表4-1.FrameworkPropertyMetadata类的属性

名称描述

AffectsArrange,

AffectsMeasure,

AffectsParentArrange,和

AffectsParentMeasure

如果为真,该依赖属性可以在布局操作过程中改变相邻元素(或父元素)的位置和布局。例如,当Margin依赖属性的AffectsMeasure值设定为真时,表示如果一个元素的margin变化,布局容器需要重复测量步骤,以确定元素的新位置。

AffectsRender如果为真,该依赖属性可以改变一个的元素绘制方式,要求该元素重绘。
BindsTwoWayByDefault如果为真,该依赖属性绑定默认值将采用双向数据绑定,而不是单向数据绑定。但是,你可以在创建绑定时另行指定你要的绑定行为。
Inherits如果为真,该依赖属性值可通过元素树传播,其嵌套元素可继承此值。例如,字体是可继承的依赖属性,如果高级别的元素设置了该值,嵌套元素继承将继承该值,除非他们明确自己的字体设置覆盖它。
IsAnimationProhibited如果为真,依赖属性不能在动画中使用。
IsNotDataBindable如果为真,依赖属性不能用绑定表达式设置。
Journal如果为真,在一个基于Page的应用程序中,依赖属性值可被保存到程序日志(访问过的网页的历史记录)。
SubPropertiesDoNotAffectRender如果为真,表示如果依赖属性的子属性(属性的属性)之一改变,WPF将不会对拥有该依赖属性的对象进行重新解析。
DefaultUpdateSourceTrigger当此属性用于一个绑定表达式时,该值为依赖属性的
Binding.UpdateSourceTrigger属性的默认值。
UpdateSourceTrigger值用于确定数据绑定值何时施加其变化。当你创建绑定时,你可以手动设置UpdateSourceTrigger属性。
DefaultValue该属性用于设定依赖属性的默认值。
CoerceValueCallback该属性提供一个试图纠正依赖属性设定值的回调函数。该方法将在依赖属性的设定值被验证之前执行。
PropertyChangedCallback该属性提供一个依赖属性值发生变化时被激活的回调函数。


在下面的章节中,您将对验证回调和一些metadata的选项进行更加深入细致的了解。您还可以通过本书介绍的几个程序示例看到它们是如何工作的。但首先,你需要了解如何才能确保每一个依赖属性均具有与传统的.NET属性相同的访问方式。


添加一个属性包装


创建一个依赖属性的最后一步是将其包装在一个传统的.NET属性中。不同的是,传统属性典型的操作过程是索取或设定一个私有字段的值,而WPF属性则使用在DependencyObject基类中定义的GetValue()和SetValue()方法索取和设定属性值。下面是一个例子:

publicThickness Margin {     set { SetValue(MarginProperty, value); }     get { return (Thickness)GetValue(MarginProperty); } }

如上面的示例所示,当创建属性包装时,你应该只需调用SetValue()和GetValue()即可。你不应该添加任何额外的代码来验证设定值或引发事件,等等。这是因为WPF的其它功能可以绕过属性包装直接调用SetValue()和GetValue()。(编译的XAML文件在运行时的解析,就是一个例子。)SetValue() 和GetValue()两个方法都是公开的。


§提示  属性包装不是一个验证数据或引发事件的正确地方。然而,WPF确实提供了添加此类代码的地方;窍门是要使用依赖属性的回调方法。验证应该通过前面所示的DependencyProperty.ValidateValueCallback来执行,而事件则可以由在下一节中所示的FrameworkPropertyMetadata.PropertyCh

 

http://jq.kvov.com.cn/jzxx41893.html