博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
依赖属性之“风云再起”三
阅读量:5968 次
发布时间:2019-06-19

本文共 10141 字,大约阅读时间需要 33 分钟。

八. 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
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
> propertyDeclarations = new Dictionary
>();
10:         //该依赖属性的键值对,键为DependencyProperty,值为object
11:         private Dictionary
properties = new Dictionary
();
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
();
122:             Dictionary
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
 ,如需转载请自行联系原作者
你可能感兴趣的文章
bash内部命令-1
查看>>
iOS在app里面跳转到系统的设置界面
查看>>
BYOD那些事
查看>>
Mandiant对APT1组织的***行动的情报分析报告
查看>>
掌握技术思想比解决问题本身更重要
查看>>
数据仓库入门(实验7)部署分析服务数据库
查看>>
SCCM2012之客户端发现与安装
查看>>
SCOM 2012系列⑨单台服务器拓扑监控
查看>>
抢先体验微软最新客户端系统Windows 8.1!
查看>>
十年IT运维谈(二)“0”和“100”
查看>>
30-35岁职场规划深谈,字字戳心
查看>>
用owncloud 打造自己的云盘
查看>>
View Horizon Mirage安装手册(一)——Horizon Mirage介绍
查看>>
Python过渡性模块重载(递归重载模块)
查看>>
Lync Server 2013之初次安装
查看>>
httpd启动报错:httpd: apr_sockaddr_info_get() failed for jsp...
查看>>
2016职业技能大赛信息安全管理与评估赛项任务书
查看>>
TSM对Oracle数据库备份脚本
查看>>
Linux系统文件目录
查看>>
用Linux Shell脚本轻松管理Radius服务器
查看>>