依赖属性的定义,分为3步(以PresentationFramework中的System.Windows.Controls.Button为例)html
1. 声明依赖属性编程
public static readonly DependencyProperty IsDefaultProperty
2. 调用DependencyProperty.Register建立依赖属性实例数组
IsDefaultProperty = DependencyProperty.Register("IsDefault", typeof(bool), typeof(Button), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, new PropertyChangedCallback(Button.OnIsDefaultChanged)));
此例中,第一个参数是依赖属性名称,第一个参数是依赖属性的值类型,第三个参数为依赖属性所在的类型,第四个参数是可选的为依赖属性提供元数据。ide
3. 为依赖属性添加传统的CLR属性封装函数
public bool IsDefault { get { return (bool) base.GetValue(IsDefaultProperty); } set { base.SetValue(IsDefaultProperty, BooleanBoxes.Box(value)); } }
为何性能
1. 声明依赖属性时为何是public、static和readonlythis
按照惯例全部的依赖属性一般都是public, static而且以Property结尾。由于是public的因此须要使用readonly来防止第三方代码对依赖属性的意外修改。spa
2. DependencyProperty.Register的第一和第二个参数code
第一个参数和第二个参数用来唯一肯定一个依赖属性,换句话说WPF为每一个依赖属性建立一个实例,该实例由依赖属性名称和其所在类型所决定,并由DependencyProperty.Register返回,能够从DependencyProperty的反编译代码中获得证明htm
private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback) { FromNameKey key = new FromNameKey(name, ownerType); lock (Synchronized) { if (PropertyFromName.Contains(key)) { throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", new object[] { name, ownerType.Name })); } }
你能够在上面的代码中看到PropertyFromName(第二行红色),PropertyFromName是一个私有的静态哈希表,用来存放使用DependencyProperty.Register注册到WPF对象层次结构中的全部(包括贡献依赖属性的全部类)依赖属性实例的静态引用。从上面代码能够看出,当name(第一个参数,依赖属性名称)和ownerType(第二参数,贡献依赖属性的类)肯定时,唯一对应PropertyFromName中的一个值(即为依赖对象实例静态引用)。
3. DependencyProperty.Register的第四个参数
第四个参数包含描述依赖属性的元数据,定制WPF处理依赖属性的行为,提供属性值改变时的回调函数和属性值的有效性验证等。
4. 传统.Net属性封装
这一步并非必须的, 应为GetValue和SetValue(后面将说明)是publish的,因此在代码中能够直接调用这两个函数(必须继承DependencyObject)。可是提供该封装能够在编程时方便使用,若是要用XAML属性中使用该依赖属性就必定要提供该封装。
它们是如何工做的
1. DependencyProperty.Register作了什么
先看一下它的反编译代码:
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback) { RegisterParameterValidation(name, propertyType, ownerType); PropertyMetadata defaultMetadata = null; if ((typeMetadata != null) && typeMetadata.DefaultValueWasSet()) { defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue); } DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback); if (typeMetadata != null) { property.OverrideMetadata(ownerType, typeMetadata); } return property; }
它调用了RegisterCommon
private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback) { FromNameKey key = new FromNameKey(name, ownerType); lock (Synchronized) { if (PropertyFromName.Contains(key)) { throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", new object[] { name, ownerType.Name })); } } if (defaultMetadata == null) { defaultMetadata = AutoGeneratePropertyMetadata(propertyType, validateValueCallback, name, ownerType); } else { if (!defaultMetadata.DefaultValueWasSet()) { defaultMetadata.DefaultValue = AutoGenerateDefaultValue(propertyType); } ValidateMetadataDefaultValue(defaultMetadata, propertyType, name, validateValueCallback); } DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback); defaultMetadata.Seal(dp, null); if (defaultMetadata.IsInherited) { dp._packedData |= Flags.IsPotentiallyInherited; } if (defaultMetadata.UsingDefaultValueFactory) { dp._packedData |= Flags.IsPotentiallyUsingDefaultValueFactory; } lock (Synchronized) { PropertyFromName[key] = dp; } if (TraceDependencyProperty.IsEnabled) { TraceDependencyProperty.TraceActivityItem(TraceDependencyProperty.Register, dp, dp.OwnerType); } return dp; }
在RegisterCommon函数中第一行红色代码使用接收的依赖属性名称和所在类的名称建立了FromNameKey实例key;第二行红色代码检测key是否在PropertyFromName中,若是存在则抛异常(WPF只为类的每一个依赖属性建立一个实例);若是key不存在,也即类的某个唯一命名依赖属性不存在,则第三行红色代码调用DependencyProperty的private构造函数为该依赖属性建立实例;最后第四行红色代码把建立的依赖属性加入到PropertyFromName中。
2. DependencyProperty的私有构造函数
private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback) { Flags uniqueGlobalIndex; this._metadataMap = new InsertionSortMap(); this._name = name; this._propertyType = propertyType; this._ownerType = ownerType; this._defaultMetadata = defaultMetadata; this._validateValueCallback = validateValueCallback; lock (Synchronized) { uniqueGlobalIndex = (Flags) GetUniqueGlobalIndex(ownerType, name); RegisteredPropertyList.Add(this); } if (propertyType.IsValueType) { uniqueGlobalIndex |= Flags.IsValueType; } if (propertyType == typeof(object)) { uniqueGlobalIndex |= Flags.IsObjectType; } if (typeof(Freezable).IsAssignableFrom(propertyType)) { uniqueGlobalIndex |= Flags.IsFreezableType; } if (propertyType == typeof(string)) { uniqueGlobalIndex |= Flags.IsStringType; } this._packedData = uniqueGlobalIndex; }
internal static int GetUniqueGlobalIndex(Type ownerType, string name) { if (GlobalIndexCount < 0xffff) { return GlobalIndexCount++; } if (ownerType != null) { throw new InvalidOperationException(SR.Get("TooManyDependencyProperties", new object[] { ownerType.Name + "." + name })); } throw new InvalidOperationException(SR.Get("TooManyDependencyProperties", new object[] { "ConstantProperty" })); }
从上面两段代码能够看出WPF为每一个DependencyProperty实例建立了一个自增的索引uniqueGlobalIndex,并把该索引和DependencyProperty值类型(使用Flags枚举来表示)一块儿封装在_packedData中。从上面代码能够看出WPF至多支持同时建立0xffff(65535)个依赖属性。
3. DependencyObject
使用依赖属性的全部类都必须继承DependencyObject,该类定义了操做依赖属性的相关方法,以下面介绍的SetValue和GetValue。DependencyObject具备一个私有的实例字段_effectiveValues用于存放依赖属性值和对应的依赖属性索引,换句话说继承自DependencyObject的类的每一个实例均维护着用于存放该类定义的经过DependencyProperty.Register注册到WPF基础结构的依赖属性值(注意不是依赖属性实例)的数组。该数组的元素为EffectiveValueEntry类型,包含依赖属性实例索引(上面已经说明全部的类实例共享一个依赖属性实例)和依赖属性值(因类的实例的不一样而不一样)的对应关系。
依赖属性和依赖属性值的存储方案以下图:
3.1 SetValue
这是由DependencyObject提供的实例方法,用于设置DependencyProperty在类实例的值。调用SetValue时WPF建立EffectiveValueEntry实例用于存放依赖属性值和依赖属性实例索引的对象关系并插入到_effectiveValues数组中,依赖属性值在_effectiveValues中是按照依赖属性的索引从小到大有序存放的(详细实现可查看DependencyObject类成员函数InsertEntry的反编译代码)。
3.2 GetValue
这是由DependencyObject提供的实例方法,用于获取DependencyProperty在类实例的值。调用GetValue时WPF根据提供的依赖属性实例索引在_effectiveValues中搜索对应的属性值,因为_effectiveValues是有序的,因此实现中使用二分法来提升搜索性能(详细实现可查看DependencyObject类成员函数LookupEntry的反编译代码)。
总结
将依赖属性从依赖属性的值上剥离,主要是为了性能上的考虑。一个WPF类可能使用几十上百个字段,而且在一次窗体呈现中该类可能被实例化不仅一次(如一个Button包含有96个字段,且在一个窗体中可能包含不少个Button),若是使用传统CLR属性方式,则将为附加到字段实例上的本地化数据分配存储空间。假设控件每一个字段的本地化数据大小平均为m,包含的字段数为f,控件被建立的次数为n,则须要的总空间为M = m * f * n,消耗的空间是直线上升的。使用依赖属性,因为依赖属性实例引用是静态的,且WPF只为依赖属性建立一个实例,因此实际所须要的空间只剩下为每一个控件实例保存依赖属性值的空间(即M=0)。
引用
《Windows.Presentation.Foundation.Unleashed》
http://www.abhisheksur.com/2011/07/internals-of-dependency-property-in-wpf.html?_sm_au_=iVVjWL1ZNjHpkVpM