八. DependencyObject测试代码
在写DependencyObject测试代码之前,我们先看一下它到底有哪些成员和方法,如下图:
通过上面的这幅图,我们知道它的主要功能包括:各种依赖属性的GetValue、SetValue操作(核心功能)和ClearValue、 CoerceValue、GetLocalValueEnumerator、ReadLocalValue等操作。为了测试这些功能,我们首先创建几个 类,第一个类X,内部首先注册一个附加依赖属性,我们都知道,不管是附加依赖属性还是依赖属性,都需要使用到GetValue和SetValue操作,只 是一个封装成了属性,而另一个封装成了静态方法而已。第二个类直接继承自我们前面在实现DependencyProperty时创建的 DependencyObject原型类。
1: class X {
2: //注册一个附加依赖属性A
3: public static readonly DependencyProperty AProperty = DependencyProperty.RegisterAttached("A", typeof(int), typeof(X));
4: //获取附加属性A的值
5: public static void SetA(DependencyObject obj, int value)
6: {
7: obj.SetValue(AProperty, value);
8: }
9: //设置附加属性A的值
10: public static int GetA(DependencyObject obj)
11: {
12: return (int)obj.GetValue(AProperty);
13: }
14: //注册一个附加依赖属性B
15: public static readonly DependencyProperty BProperty = DependencyProperty.RegisterAttached("B", typeof(string), typeof(X));
16: //设置附加属性B的值
17: public static void SetB(DependencyObject obj, string value)
18: {
19: obj.SetValue(BProperty, value);
20: }
21: //获取附加属性B的值
22: public static string GetB(DependencyObject obj)
23: {
24: return (string)obj.GetValue(BProperty);
25: }
26:
27: }
28:
29: class Y : DependencyObject {
30: }
第三个类则是为了直接测试注册一个依赖属性,这个类首先继承自DependencyObject原型类。
1: class Z : DependencyObject
2: {
3: public static readonly DependencyProperty SimpleDPProperty =
4: DependencyProperty.Register("SimpleDP", typeof(double), typeof(Z),
5: new PropertyMetadata((double)0.0,
6: new PropertyChangedCallback(OnValueChanged),
7: new CoerceValueCallback(CoerceValue)),
8: new ValidateValueCallback(IsValidValue));
9:
10: public double SimpleDP
11: {
12: get { return (double)GetValue(SimpleDPProperty); }
13: set { SetValue(SimpleDPProperty, value); }
14: }
15:
16: private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
17: {
18: Console.WriteLine("当值改变时,我们可以做的一些操作,具体可以在这里定义: {0}", e.NewValue);
19: }
20:
21: private static object CoerceValue(DependencyObject d, object value)
22: {
23: Console.WriteLine("对值进行限定,强制值: {0}", value);
24: return value;
25: }
26:
27: private static bool IsValidValue(object value)
28: {
29: Console.WriteLine("验证值是否通过,如果返回True表示验证通过,否则会以异常的形式暴露: {0}", value);
30: return true;
31: }
32:
33: }
首先我们先写测试GetValue和SetValue操作的测试代码,然后不能通过,最后完善DependencyObject类的GetValue和SetValue方法直到测试用例通过。
1: [Test]
2: [Category ("NotWorking")]
3: public void TestAttachedProperty()
4: {
5: Y y1 = new Y();
6: X.SetA(y1, 2);
7: Assert.AreEqual(2, X.GetA(y1));
8: }
由于这里是y1和y2两个对象,所以他们的GetValue和SetValue也是设置和取得各自的值。
1: [Test]
2: [Category ("NotWorking")]
3: public void Test2AttachedProperties()
4: {
5: Y y1 = new Y();
6: Y y2 = new Y();
7: X.SetA(y1, 2);
8: X.SetA(y2, 3);
9: Assert.AreEqual(2, X.GetA(y1));
10: Assert.AreEqual(3, X.GetA(y2));
11: }
通过前面的图,大家可以看到DependencyObject提供了一个取得本地值枚举器的GetLocalValueEnumerator方法,它实现一个IEnumerator来方便访问LocalValue,这里我们要实现它,所以先写测试代码。
1: [Test]
2: [Category ("NotWorking")]
3: public void TestEnumerationOfAttachedProperties()
4: {
5: int count = 0;
6: Y y = new Y();
7: X.SetA(y, 2);
8: X.SetB(y, "Hi");
9:
10: //根据DependencyObject得到所有本地值
11: LocalValueEnumerator e = y.GetLocalValueEnumerator();
12: while (e.MoveNext()) {
13: count++;
14: if (e.Current.Property == X.AProperty)
15: Assert.AreEqual(e.Current.Value, 2);
16: else if (e.Current.Property == X.BProperty)
17: Assert.AreEqual(e.Current.Value, "Hi");
18: else
19: Assert.Fail("Wrong sort of property" + e.Current.Property);
20: }
21: //count为2
22: Assert.AreEqual(2, count);
23: }
还有几个功能,既然Mono也没做研究,我们也就不费那个力气了,接下来我们就看看刚才实现的DependencyObject代码吧!
九. DependencyObject实现代码
通过前面的测试用例,DependencyObject类的基本功能已经完成,不过我们要注意几个要点:
1,依赖属性其实终究要DependencyObject和DependencyProperty成对才能算得上真正的DependencyProperty
1,依赖属性其实终究要DependencyObject和DependencyProperty成对才能算得上真正的DependencyProperty
2,不管是Register、RegisterAttached、RegisterAttachedReadOnly还是 RegisterReadOnly操作,我们都要通过DependencyObject来操作DependencyProperty的值,也就是通过 DependencyObject这个外部接口来操作,DependencyProperty只负责注册和内部处理,不负责外部接口。
3,在DependencyObject中提供了几个操作LocalValue的接口的接口,其中包括ReadLocalValue、GetLocalValueEnumerator、CoerceValue和ClearValue等。
4,在注册注册依赖属性时,实质是关联DependencyObject的propertyDeclarations,它是一个 Dictionary<Type,Dictionary<string,DependencyProperty>>类型,但是在 register代码中并没有完全关联起来,我也比较纳闷,所以这点还希望和大家一起探讨,微软的BCL并没有这么实现。
1: using System.Collections.Generic;
2: //using System.Windows.Threading;
3:
4: namespace System.Windows
5: {
6: public class DependencyObject
7: {
8: //依赖属性其实终究要DependencyObject和DependencyProperty成对才能算得上真正的DependencyProperty
9: private static Dictionary<Type,Dictionary<string,DependencyProperty>> propertyDeclarations = new Dictionary<Type,Dictionary<string,DependencyProperty>>();
10: //该依赖属性的键值对,键为DependencyProperty,值为object
11: private Dictionary<DependencyProperty,object> properties = new Dictionary<DependencyProperty,object>();
12:
13: //是否已密封,没有实现DependencyObject层次的IsSealed判断
14: public bool IsSealed {
15: get { return false; }
16: }
17:
18: //获取该DependencyObject的DependencyObjectType
19: public DependencyObjectType DependencyObjectType {
20: get { return DependencyObjectType.FromSystemType (GetType()); }
21: }
22:
23: //根据该依赖属性名,清除它的值
24: public void ClearValue(DependencyProperty dp)
25: {
26: if (IsSealed)
27: throw new InvalidOperationException ("Cannot manipulate property values on a sealed DependencyObject");
28:
29: properties[dp] = null;
30: }
31:
32: //根据该依赖属性DependencyPropertyKey,清除它的值
33: public void ClearValue(DependencyPropertyKey key)
34: {
35: ClearValue (key.DependencyProperty);
36: }
37:
38: //根据该依赖属性名,强制值
39: public void CoerceValue (DependencyProperty dp)
40: {
41: PropertyMetadata pm = dp.GetMetadata (this);
42: if (pm.CoerceValueCallback != null)
43: pm.CoerceValueCallback (this, GetValue (dp));
44: }
45:
46: public sealed override bool Equals (object obj)
47: {
48: throw new NotImplementedException("Equals");
49: }
50:
51: public sealed override int GetHashCode ()
52: {
53: throw new NotImplementedException("GetHashCode");
54: }
55:
56: //得到本地值的枚举器
57: public LocalValueEnumerator GetLocalValueEnumerator()
58: {
59: return new LocalValueEnumerator(properties);
60: }
61:
62: //根据依赖属性名获取值
63: public object GetValue(DependencyProperty dp)
64: {
65: object val = properties[dp];
66: return val == null ? dp.DefaultMetadata.DefaultValue : val;
67: }
68:
69:
70: public void InvalidateProperty(DependencyProperty dp)
71: {
72: throw new NotImplementedException("InvalidateProperty(DependencyProperty dp)");
73: }
74:
75: //当属性值改变时,触发回调
76: protected virtual void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
77: {
78: PropertyMetadata pm = e.Property.GetMetadata (this);
79: if (pm.PropertyChangedCallback != null)
80: pm.PropertyChangedCallback (this, e);
81: }
82:
83: //提供一个外界查看LocalValue的接口
84: public object ReadLocalValue(DependencyProperty dp)
85: {
86: object val = properties[dp];
87: return val == null ? DependencyProperty.UnsetValue : val;
88: }
89:
90: //根据依赖属性名设置其值
91: public void SetValue(DependencyProperty dp, object value)
92: {
93: if (IsSealed)
94: throw new InvalidOperationException ("Cannot manipulate property values on a sealed DependencyObject");
95:
96: if (!dp.IsValidType (value))
97: throw new ArgumentException ("value not of the correct type for this DependencyProperty");
98:
99: ValidateValueCallback validate = dp.ValidateValueCallback;
100: if (validate != null && !validate(value))
101: throw new Exception("Value does not validate");
102: else
103: properties[dp] = value;
104: }
105:
106: //根据依赖属性DependencyPropertyKey设置其值
107: public void SetValue(DependencyPropertyKey key, object value)
108: {
109: SetValue (key.DependencyProperty, value);
110: }
111:
112: protected virtual bool ShouldSerializeProperty (DependencyProperty dp)
113: {
114: throw new NotImplementedException ();
115: }
116:
117: //这里的注册实质是关联propertyDeclarations
118: internal static void register(Type t, DependencyProperty dp)
119: {
120: if (!propertyDeclarations.ContainsKey (t))
121: propertyDeclarations[t] = new Dictionary<string,DependencyProperty>();
122: Dictionary<string,DependencyProperty> typeDeclarations = propertyDeclarations[t];
123: if (!typeDeclarations.ContainsKey(dp.Name))
124: {
125: typeDeclarations[dp.Name] = dp;
126: //这里仍然有一些问题,期待各位共同探讨解决
127: }
128: else
129: throw new ArgumentException("A property named " + dp.Name + " already exists on " + t.Name);
130: }
131: }
132: }
通过前面对DependencyObject和DependencyProperty的研究之后,我们来看看最重要的一个角色,这也是微软最喜欢用的概念——元数据,如果大家研究过微软BCL的源码,应该都知道,它是贯穿于整个CLR当中的。
本文转自KnightsWarrior51CTO博客,原文链接:http://blog.51cto.com/knightswarrior/405232
,如需转载请自行联系原作者