public class ValidationParams:DependencyObject { public object Param1 { get { return (object)GetValue(Param1Property); } set { SetValue(Param1Property, value); } } // 堆代码 duidaima.com // Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc... public static readonly DependencyProperty Param1Property = DependencyProperty.Register("Param1", typeof(object), typeof(ValidationParams), new PropertyMetadata(null)); }代码中Param1Property才是真正的依赖属性,Param1是依赖属性的包装器,这里有一个命名约定,依赖属性的名称是对应包装器名称+Property组成。在Visual studio中输入propdp,然后Tab键就会自动生成依赖属性以及包装器的代码片段,然后根据实际情况修改相应的参数和类型。
从修饰符可以看出依赖属性是一个静态的只读变量,要确保不同实例的依赖属性正确赋值,肯定不能把数据直接保存到这个静态变量中。这里其实也是依赖属性机制的核心。
DependencyProperty:依赖属性实例都是单例,其中DefaultMetadata存储了依赖属性的默认值,提供变化通知、限制、检验等回调以及子类override依赖属性的渠道。GlobalIndex用于检索DependencyProperty的实例。应用程序中注册的所有DependencyProperty的实例都存放于名为PropertyFromName的Hashtable中。
DependencyObject:依赖属性的宿主对象,_effectiveValues是一个私有的有序数组,用来存储本对象实例中修改过值得依赖属性,GetValue、SetValue方法用于读写依赖属性的数值。
private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback) { FromNameKey key = new FromNameKey(name, ownerType); .....略去校验以及默认元数据代码 // Create property DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback); // Map owner type to this property // Build key lock (Synchronized) { PropertyFromName[key] = dp; } return dp; }
代码的大致意思是生成一个FromNameKey类型的key,然后构造一个DependencyProperty实例dp,并存放到名为PropertyFromName的Hashtable中,最后返回这个实例dp。
FromNameKey是DependencyProperty中的内部私有类,其代码如下: private class FromNameKey { public FromNameKey(string name, Type ownerType) { _name = name; _ownerType = ownerType; _hashCode = _name.GetHashCode() ^ _ownerType.GetHashCode(); } public override int GetHashCode() { return _hashCode; } ...略去部分代码 private string _name; private Type _ownerType; private int _hashCode; }这里特地介绍这个类是因为FromNameKey对象是依赖属性实例的key,它的hashcode是由Register的第一个参数(依赖属性包装器属性名称字符串)的hashcode和第三个参数(依赖属性宿主类型)的hashcode做异或运算得来的,这样设计确保了每个DependecyObject类型中不同名称的依赖属性的实例是唯一的。
public object GetValue(DependencyProperty dp) { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); ArgumentNullException.ThrowIfNull(dp); // Call Forwarded return GetValueEntry( LookupEntry(dp.GlobalIndex), dp, null, RequestFlags.FullyResolved).Value; }方法前几行是线程安全性和参数有效性检测,最后一行是获取依赖属性的值。LookupEntry是根据DependencyProperty实例的GlobalIndex在_effectiveValues数组中查找依赖属性的有效值EffectiveValueEntry,找到后返回其索引对象EntryIndex。EntryIndex主要包含Index和Found两个属性,Index表示查找到的索引值,Found表示是否找到目标元素。
前边提到依赖属性支持多属性值,WPF中可以通过多种方法为一个依赖项属性赋值,如通过样式、模板、触发器、动画等为依赖项属性赋值的同时,控件本身的声明也为属性进行了赋值。在这种情况下,WPF只能选择其中的一种赋值作为该属性的取值,这就涉及到取值的优先级问题。
从上一小节的图中可以看到EffectiveValueEntry中有两个属性:ModifiedValue和BaseValueSourceInternal,ModifiedValue用于跟踪依赖属性的值是否被修改以及被修改的状态。BaseValueSourceInternal是一个枚举,它用于表示依赖属性的值是从哪里获取的。在与ModifiedValue一起使用,可以确定最终呈现的属性值。
internal EffectiveValueEntry GetFlattenedEntry(RequestFlags requests) { ......略去部分代码 // Note that the modified values have an order of precedence // 1. Coerced Value (including Current value) // 2. Animated Value // 3. Expression Value // Also note that we support any arbitrary combinations of these // modifiers and will yet the precedence metioned above. if (IsCoerced) { ......略去部分代码 } else if (IsAnimated) { ......略去部分代码 } else { ......略去部分代码 } return entry; }其中表达式值包含样式、模板、触发器、主题、控件本身对属性赋值或者绑定表达式。其优先级则是在BaseValueSourceInternal中定义的。枚举元素排列顺序与取值优先级顺序刚好相反。
// Note that these enum values are arranged in the reverse order of // precendence for these sources. Local value has highest // precedence and Default value has the least. Note that we do not // store default values in the _effectiveValues cache unless it is // being coerced/animated. [FriendAccessAllowed] // Built into Base, also used by Core & Framework. internal enum BaseValueSourceInternal : short { Unknown = 0, Default = 1, Inherited = 2, ThemeStyle = 3, ThemeStyleTrigger = 4, Style = 5, TemplateTrigger = 6, StyleTrigger = 7, ImplicitReference = 8, ParentTemplate = 9, ParentTemplateTrigger = 10, Local = 11, }综合起来依赖属性取值优先级列表如下:
依赖属性元数据中的默认值