Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能,这些服务通常统称为 WPF 属性系统。由 WPF 属性系统支持的属性称为依赖项属性,虽然不清楚依赖属性,但是属性我们是很清楚的,封装类的字段,表示类的状态,编译后被转化为get_,set_方法,可以被类或结构等使用,常见的一个属性如下:
1 2 3 4 5 6 7 8 9
| public class ClassObject { private string name; public string Name { get { return name; } set { name = value; } } }
|
既然已经有了属性,为什么还要有依赖属性呢?必然是属性有一些缺点了,而依赖属性恰好能够解决这个问题。以 Button 为例:
每次继承,父类的私有字段都被继承下来。对 Button 来说,大多数属性并没有被修改,仍然保持父类定义时的默认值。通常情况下,在整个 Button 的对象生命周期中,也只有少部分属性被修改。也已看得出来:
每次继承,子类对象都会获得更多的属性,这样,继承树的低端对象不可避免的膨胀;
既然大多数属性没有被修改,那么就可以把这些属性从对象中剥离,减少对象的体积;
可以知道,依赖属性就是为了解决这个问题诞生了。首先定义依赖属性,它里面存储之前 2 中希望剥离的属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class DependencyProperty { internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>(); internal string Name; internal object Value; internal object HashCode;
private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue) { this.Name = name; this.Value = defaultValue; this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode(); } public static DependencyProperty Register(string name, Type propertyName, Type ownerType, object defaultValue) { DependencyProperty dp = new DependencyProperty(name, propertyName, ownerType, defaultValue); RegisteredDps.Add(dp.HashCode, dp); return dp; } }
|
然后定义 DependencyObject 来使用 DP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class DependencyObject { public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty);
public object GetValue(DependencyProperty dp) { return DependencyProperty.RegisteredDps[dp.HashCode].Value; }
public void SetValue(DependencyProperty dp, object value) { DependencyProperty.RegisteredDps[dp.HashCode].Value = value; }
public string Name { get { return (string)GetValue(NameProperty); } set { SetValue(NameProperty, value); } } }
|
DependencyObject 和文章开篇的 ClassObject 中的 Name 有什么不同呢?
DependencyObject.Name 的实际值不是用字段保存在 DependencyObject 中,而是保存在 NameProperty 中,通过 SetValue 和 GetValue 来金星赋值取值操作。
在上述例子中,所有 DependncuObject 的对象将共用一个 NameProperty,这在现实中是不实际的:只要修改一个对象的属性,相当于所有对象的属性值都被修改了。所以,修改的属性,还是要维护在相应的对象中的:(修改部分用 ☆ 表示)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class DependencyProperty { private static int globalIndex = 0; internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>(); internal string Name; internal object Value; internal int Index; internal object HashCode;
private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue) { this.Name = name; this.Value = defaultValue; this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode(); } public static DependencyProperty Register(string name, Type propertyName, Type ownerType, object defaultValue) { DependencyProperty dp = new DependencyProperty(name, propertyName, ownerType, defaultValue); globalIndex++; dp.Index = globalIndex; RegisteredDps.Add(dp.HashCode, dp); return dp; } }
|
所有修改过的 DP 都保存在 EffectiveValueEntry 里,这样,就可以只保存修改的属性,未修改过的属性仍然读取 DP 的默认值,优化了属性的储存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public class DependencyObject { private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>(); public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty);
public object GetValue(DependencyProperty dp) { EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault(i => i.PropertyIndex == dp.Index); if (effectiveValue.PropertyIndex != 0) { return effectiveValue.Value; } else { return DependencyProperty.RegisteredDps[dp.HashCode].Value; } }
public void SetValue(DependencyProperty dp, object value) { EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault(i => i.PropertyIndex == dp.Index); if (effectiveValue.PropertyIndex != 0) { effectiveValue.Value = value; } else { effectiveValue = new EffectiveValueEntry() { PropertyIndex = dp.Index, Value = value }; } }
public string Name { get { return (string)GetValue(NameProperty); } set { SetValue(NameProperty, value); } } }
|
1 2 3 4 5
| internal struct EffectiveValueEntry { internal int PropertyIndex{get;set;} internal object Value{get;set;} }
|