打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
附加属性

3.2.5  附加属性

附加属性是依赖属性的一种特殊形式,可以被有效地添加到任何对象中。一开始,这可能听上去很奇怪,但是这个机制在WPF中有多种应用。

拿About对话框的例子来说,想象如果不设置整个Windows元素的FontSize和FontStyle(在代码清单3-4中),而是在内部的StackPanel上设置它们的话,这两个属性仅仅会被两个Button继承。然而,把属性特性移到内部的StackPanel元素中没有什么作用,因为StackPanel自己没有任何与字体相关的属性。相反,你必须使用FontSize和FontStyle附加属性,这是在一个叫作TextElement的类中定义的。代码清单3-5演示了如何使用这两个附加属性,其中还介绍了一种新的XAML语法,这是专门为附加属性设计的。这样就能启用一些我们所盼望的属性值继承特性,如图3-6所示。







图3-6  About对话框,其中两个按钮上都带有FontSize和
FontStyle属性,都是从StackPanel继承而来的

代码清单3-5  About对话框,把Font属性移到了内部的StackPanel中







TextElement.FontSize和TextElement.FontStyle(而不是简单的FontSize和FontStyle属性)必须在StackPanel元素中使用,因为StackPanel没有这两个属性。当XAML解析器或者编译器遇到这种语法时,它就要求TextElement(有时叫作附加属性提供者)有两个静态方法分别叫作SetFontSize和SetFontStyle,这样它们才可以设置相应的属性值。代码清单3-5中列出的StackPanel的声明与下面的C#代码是一样的:







注意,像FontStyles.Italic、Orientation.Horizontal和HorizontalAlignment.Center这样的枚举值,在之前的XAML中被简单地写成了Italic、Horizontal和Center。之所以可以这么做是因为有.NET Framework中的EnumConverter类型转换器,它可以转换所有不区分大小写的字符串。


虽然代码清单3-5中的XAML很好地把FontSize和FontStyle作为StackPanel的逻辑附加属性,但是C#代码告诉我们这里并没有什么神奇之处,仅仅是调用了与一个元素相关联的方法,而那个元素中有一个与其他东西没有关系的属性。说到附加属性,有趣的事情之一就是.NET属性都不是附加属性!


从内部看,SetFontSize这样的方法会只是调用DependencyObject.SetValue方法,通常每一个普通的依赖属性访问器都会调用同一个方法,但是这里调用的是传进来的DependencyObject上的DependencyObject.SetValue方法,而不是当前实例上的:





与此类似,附加属性也定义了一个静态方法叫作GetXXX(XXX是属性的名称),它会调用我们所熟悉的DependencyObject.GetValue方法:





与普通依赖属性的属性包装器一样,这些GetXXX和SetXXX方法只能调用GetValue和SetValue方法,不能挪作他用。

理解附加属性提供程序

代码清单3-5中所使用的FontSize和FontStyle附加属性,最令人困惑的部分是,它们并不是在Button或者Control中定义的,而这些却是定义普通FontSize和FontStyle依赖属性的基类。它们是看似不相关的TextElement类定义的(还有在TextBlock类中定义,其实在前面的示例中也可以使用TextBlock类)。

既然TextElement.FontSizeProperty是一个与Control.FontSizeProperty无关的Depen- dencyProperty成员(而TextElement.FontStyleProperty也是与Control.FontStyleProperty无关的),那么这一切是如何实现的呢?关键在于这些依赖属性在内部注册的方式。如果你看看TextElement的源代码,就会发现类似下面的东西:





这与之前那个注册Button的IsDefault依赖属性的示例很相似,但这里RegisterAttached方法对附加属性的属性元数据(property metadata)的处理作了优化。

从另一方面讲,Control不能注册FontSize依赖属性!它需要调用TextElement已经注册的属性的AddOwner方法,获得对同一个实例的引用:





因此,所有控件继承的FontSize、FontStyle属性和其他与字体相关的依赖属性都是由TextElement提供的属性!

幸运的是,在大多数情况下,提供附加属性(如GetXXX和SetXXX方法)的类就是定义一些通用依赖属性的类,从而避免了混淆。

虽然About对话框示例为了实现高级属性值的继承使用了附加属性,但附加属性通常都是用于用户界面元素的布局。(事实上,附加属性一开始是为WPF的布局系统而设计的。)派生自Panel的各种类定义了一些附加属性,用来把它们添加到子元素上来控制它们的摆放。通过这种方式,每个Panel可以把自定义行为给任何一个子元素,而不需要所有的子元素都具有自己的相关属性集。这种方式也让像布局这样的系统扩展起来更加简单方便,因为任何人都可以写一个带有自定义附加属性的新Panel控件。这在第6章和第17章中将有详细讲解。把附加属性作为一种扩展机制类似于以往Windows Forms那样的技术,许多WPF类定义了一个Tag属性(类型是System.Object),目的是为了存储每一个实例的自定义数据。但是要添加自定义数据给任何一个派生自DependencyObject的对象,附加属性是一种更加强大、更加灵活的机制。通常我们会忽略一点,即你可以用附加属性高效地向密封类(sealed class)的实例添加自定义数据(WPF中到处都是密封类)。另外,大家对附加属性有一个曲解,虽然在XAML中设置它们依赖于SetXXX静态方法,但是可以在过程式代码中绕过这个方法,直接去调用DependencyObject.SetValue方法。这意味着在过程式代码中,可以把任何一个依赖属性作为一个附加属性。例如,下面这行代码把ListBox的IsTextSearchEnabled属性添加到了Button控件上,并赋予该属性一个值:





虽然这似乎没有任何意义,也不会给这个Button带来一些神奇的新功能,但是你可以用一种对应用程序或者组件有意义的方式来随意使用这个属性值。

这种方法还有很多有趣的方式可以用来扩展元素。例如,FrameworkElement的Tag属性是一个依赖属性,因此可以把一个GeometryModel3D(在12.1节中将会再次见到这个类,它是一个密封类,没有Tag属性)的对象实例附加给它。





这仅仅是WPF提供可扩展性的一种方式之一,但不需要用到传统的继承特性。



     
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
闲话WPF之十二(Attached属性 )
WPF里的DependencyProperty(5)
WPF依赖属性详解
WPF之路
WPF中Image的Stretch属性
WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服