|
一. 摘要
首先圣殿騎士很高興”WPF 基礎到企業應用系列” 能得到大家的關注、支持和認可。看到很多朋友留言希望加快速度的問題,我會盡力的,對你們的熱情關注也表示由衷的感謝。這段時間更新慢的主要原因是因為忙著用TDD還原MONO的框架,同時也因為一直在研究云計算,所以就拖拖拉拉一直沒有發布后面的文章。由于WPF整個系列是自己的一些粗淺心得和微薄經驗,所以不會像寫書那么面面俱到,如果有不足或者錯誤之處也請大家見諒。在今年之內圣殿騎士會盡量完成”WPF 基礎到企業應用系列”和”云計算之旅系列“,誠然,由于本人才識淺薄,所以熱切希望和大家共勉!
由于依賴屬性是WPF和Silverlight的核心概念,微軟在C/S和B/S平臺上主要精力都放到了WPF和Silverlight技術上,同時Silverlight也是Windows Phone的兩大編程模型之一(另外一種是XNA),所以我們花費了大量的時間和篇幅進行論述。在上一篇WPF基礎到企業應用系列7——深入剖析依賴屬性中,我們首先從依賴屬性基本介紹講起,然后過渡到依賴屬性的優先級、附加屬性、只讀依賴屬性、依賴屬性元數據、依賴屬性回調、驗證及強制值、依賴屬性監聽、代碼段(自動生成) 等相關知識,最后我們模擬一個WPF依賴屬性的實現,由于上篇是根據微軟WPF的BCL源碼剖析的,所以這篇我們就研究一下.NET的跨平臺版本MONO,看下它是怎么來實現這個依賴屬性機制。
二. 本文提綱
· 1.摘要
· 2.本文提綱
· 3.兵馬未動、廢話先行
· 4.依賴屬性續前緣
· 5.引入測試驅動開發
· 6.DependencyProperty測試代碼
· 7.DependencyProperty實現代碼
· 8.DependencyObject測試代碼
· 9.DependencyObject實現代碼
· 10.PropertyMetadata測試代碼
· 11.PropertyMetadata實現代碼
· 12.其他協助類測試代碼
· 13.其他協助類的實現代碼
· 14.回歸并統計覆蓋率
· 15.簡單驗證依賴屬性系統
· 16.本文總結
· 17.相關代碼下載
· 18.系列進度
三. 兵馬未動,廢話先行
在講這篇文章之前,我們先來拉一拉家常,說點題外話,就當進入正餐之前的一些甜點,當然這里主要針對.NET平臺而言:
1,淺談軟件技術的發展趨勢及定位
互聯網的普及應用催生了很多技術的發展與更新,如果仔細深究,你會發現軟件技術的發展趨勢將主要體現在以下四個方面:客戶端軟件開發(其中包括客戶端軟件、游戲、中間件和嵌入式開發等)、Web 開發(包括傳統的Web技術、Web游戲以及一些在線應用)、移動設備軟件開發(主要涉及到手機等移動設備)、云計算開發(公有云、私有云、混合云會逐漸界限清晰,云廠商以及云平臺也會逐漸整合和成熟起來)。就微軟來說,這四個方面主要如下:
◆ 客戶端軟件開發
目前微軟主要有Win32 應用程序、MFC 應用程序、WinForm應用程序和WPF 應用程序作為開發選擇,目前這四種技術還會共存,因為不同的需求以及不同的人群都有不同的需要。當然WPF借助于其強大的功能和迅猛的發展速度很快會成為首選,這個是值得肯定的。
◆ Web 開發
在WEB方面微軟主要有ASP.NET、ASP.NET MVC、Silverlight三種技術,ASP.NET技術已經發展了多年,在未來的很長一段時間內還會是主流,同時結合Silverlight作為局部和整體應用效果都還很不錯,所以這也是很多企業的首選。ASP.NET MVC在目前來說應用還不是特別廣泛,不過用過之后感覺也還不錯,只是還需要一段時間的適應過程而已。Silverlight在構建局部應用和整站應用都發揮了不錯的優勢,在Windows Phone中也表現得不錯,所以這個技術將會一直熱下去。
◆ 移動設備軟件開發
移動設備方面可謂是現在眾廠商競爭最激烈的市場之一,也是傳統技術和新型技術的主要戰場之一。微軟現在主推的Windows Phone開發主要包括Silverlight和XNA兩種技術,Windows Phone開發逐漸變得和ASP.NET開發一樣簡單,這也是微軟的一個目標。
◆ 云計算開發
云計算現在基本上成了互聯網的第一大熱門詞,不管是軟件為主導的企業,還是以硬件為主導的企業,都卷入了這場紛爭與革命。微軟的云平臺——Windows Azure Platform,它是微軟完整的云計算平臺,目前包含了如下三大部分(Windows Azure:運行在云中的操作系統,對于用戶來說是虛擬且透明的,其中提供了Compute(計算),Storage(存儲),以及Manage(管理)這三個主要功能及其底層服務,使用起來相當的便捷。SQL Azure:運行于云中的一個關系數據庫,和SQL Server 2008類似,但是在功能上還沒有那么強大。AppFabric:全名是Windows Azure platform AppFabric,提供了訪問控制、服務總線等服務,主要用于把基礎應用連接到云中)。
其實把這四個方面總結起來就是傳說中的微軟“三屏一云”戰略,從中也可以看出微軟逍遙于天地,縱橫于宇內,嘯傲于世間,雄霸于大地的梟雄戰略!
2,淺談微軟跨平臺與MONO
在談之前我們先看一下什么是MONO?MONO項目是由Ximian發起、Miguel de lcaza領導、Novell公司主持的項目。它是一個致力于開創.NET在Linux,FreeBSD,Unix,Mac OS X和Solaris等其他平臺使用的開源工程。它包含了一個C#語言的編譯器,一個CLR的運行時,和一組類庫,并逐漸實現了 ADO.NET、ASP.NET、WinForm、Silverlight(可惜沒有實現強大的WPF),能夠使得開發人員在其他平臺用C#開發程序。
◆ 值得看好的地方:
1,跨平臺:開創.NET在Linux,FreeBSD,Unix,Mac OS X和Solaris等其他平臺使用,這是微軟沒有實現的,但是MONO進行了補充,所以值得看好。
2,開源:不論使用什么技術,大家似乎都希望能夠用開源的產品,一方面是考慮到技術的可控性和可維護性;另一方面則是考慮到安全性,當然在另一個角度也是可以學習到其中的一些技術和思想,所以大家對開源總是報以歡迎的態度。
3,不同的方式實現.NET框架:由于微軟對技術申請了專利,所以MONO不能盲目的模仿,對很多細節都改用自己的方式進行了實現,所以我們也可以學到很多不一樣的實現方式。
4,持續更新:MONO從一開始到現在始終在更新,其中包括bug修復、版本升級、增加新的功能及應用,所以相信它會在不斷的更新中更加完善。
◆ 不足之處:
1.模仿但要避免專利:由于是模仿微軟.NET平臺,但因為微軟對代碼申請了專利,所以MONO只能采用其它實現方式來實現同樣的功能,這樣一來很多地方就會實現得很累贅,效率也會受損。
2.沒有擺脫實驗產品的頭銜:由于它目前的使用比較低,所以信息反饋和持續改進就做得比較弱,這也是目前功能完善得比較慢的原因之一吧。
3,功能還需要完善:一些主要功能還未實現,如作為Windows平臺最基礎的COM和COM+功能沒有保存,像MSMQ等消息隊列,消息傳送的功能也沒有實現,對ADO.NET、XML等核心功能效率有待提升,對BCL庫代碼也有很多需要優化的地方,強大的WPF也沒有引入。
4.效率和用戶體驗還有待提升。
◆ 與微軟之間的關系
微軟與MONO之間的關系也一直處于不冷不熱的狀態,沒有明確的反對,也沒有明確的支持,究其原因筆者認為主要有以下兩點:
1,微軟帶來最大收益的產品仍舊是Windows操作系統和Office等軟件,微軟在其他領域盈利都沒有這兩大產品來得直接。而.NET作為微軟的強大開發平臺,是不希望落在其他平臺上運行的,這樣就會削弱Windows操作系統和Office等軟件的市場占有率,所以讓.NET跨平臺對微軟來說是一件舍本求末的事情,這也是微軟不主張.NET運行于其他平臺的主要原因,你想微軟是一個以技術為主導的公司,任何IT市場都會有它的身影,如果想讓.NET跨平臺,那豈不是一件很輕而易舉的事情嗎?
2,由于MONO還沒有成熟,在很多方面都表現得像一個實驗室產品,在根本上沒有對微軟構成威脅,況且在外界質疑.NET是否能跨平臺的時候,還有一個現身的說法,所以微軟也不會明確的反對和支持。
◆ 總結
雖然目前來說MONO喜憂參半,但優點始終要大于缺點,畢竟每一個框架或者產品都是慢慢不斷改進而完善的,更何況開源必將是未來的一個趨勢,所以我們有理由也有信心期待它接下來的發展。
3,談談源碼研究與TDD
大家都有一個共識:如果你想研究某個框架或者工具的源碼,那先必須熟練使用它,熟練之后自然就有一種研究它的沖動,但是往往這個框架或工具比較龐大,很不容易下手,一個很不錯的方法就是使用TDD。我們都知道TDD的基本思想就是在開發功能代碼之前,先編寫測試代碼。也就是說在明確要開發某個功能后,首先思考如何對這個功能進行測試,并完成測試代碼的編寫,然后編寫相關的代碼滿足這些測試用例。然后循環進行添加其他功能,直到完全部功能的開發,在此過程中我們可以借助一些工具來協助。比如我們現在要研究Nhibernate,那么我們首先要熟練它的一些功能,然后從一個點出發慢慢編寫單元測試,然后逐漸完善代碼,最后直至完成框架的搭建,這樣會給我們帶來莫大的驅動力和成就感。除了微軟的BCL(Base Class Library)和企業庫以外,大家還可以用TDD來試試還原以下的任一開源代碼:
Spring.NET(http://www.springframework.NET/)、Castle(http://www.castleproject.org)、log4NET(http://logging.apache.org/log4NET/)、
NHibernate(http://www.hibernate.org/343.html)、iBATIS.NET(http://ibatis.apache.org)、Caliburn(http://caliburn.codeplex.com/)、
MVVM Light Toolkit(http://mvvmlight.codeplex.com/)、Prism(http://compositewpf.codeplex.com/)、MONO源碼(www.mono-project.com)
四. 依賴屬性續前緣
大家都知道WPF和Silverlight帶來了很多新的特性,其中一大亮點是引入了一種新的屬性機制——依賴屬性。依賴屬性基本應用在了WPF的所有需要設置屬性的元素。依賴屬性根據多個提供對象來決定它的值(可以是動畫、父類元素、綁定、樣式和模板等),同時這個值也能及時響應變化。所以WPF擁有了依賴屬性后,代碼寫起來就比較得心應手,功能實現上也變得非常容易了。如果沒有依賴屬性,我們將不得不編寫大量的代碼。依賴屬性在WPF中用得非常廣泛,具體在以下幾個方面中表現得尤為突出:
◆ UI的強大屬性體系
◆ Property value inheritance(值繼承)
◆ Metadata(強大的元數據)
◆ 屬性變化通知,限制、驗證
◆ Resources(資源)
◆ Data binding(數據綁定)
◆ Styles、Template(樣式、模板和風格)
◆ 路由事件、附加事件、附加行為乃至命令
◆ Animations、3D(動畫和3D)
◆ WPF Designer Integration(WPF設計、開發集成)
在上一篇WPF基礎到企業應用系列7——深入剖析依賴屬性中,我們對依賴屬性做了較詳細的介紹,那么下面我們就簡單回顧一下,其實依賴屬性的實現很簡單,只要做以下步驟就可以實現:
◆ 第一步: 讓所在類型繼承自 DependencyObject基類,在WPF中,我們仔細觀察框架的類圖結構,你會發現幾乎所有的 WPF 控件都間接繼承自DependencyObject類型。
◆ 第二步:使用 public static 聲明一個 DependencyProperty的變量,該變量才是真正的依賴屬性 ,看源碼就知道這里其實用了簡單的單例模式的原理進行了封裝(構造函數私有),只暴露Register方法給外部調用。
◆ 第三步:在靜態構造函數中完成依賴屬性的元數據注冊,并獲取對象引用,看代碼就知道是把剛才聲明的依賴屬性放入到一個類似于容器的地方,沒有講實現原理之前,請容許我先這么陳述。
◆ 第四步:在前面的三步中,我們完成了一個依賴屬性的注冊,那么我們怎樣才能對這個依賴屬性進行讀寫呢?答案就是提供一個依賴屬性的實例化包裝屬性,通過這個屬性來實現具體的讀寫操作。
根據前面的四步操作,我們就可以寫出下面的代碼:
1: public class SampleDPClass : DependencyObject
2: {
3: //聲明一個靜態只讀的DependencyProperty字段
4: public static readonly DependencyProperty SampleProperty;
5:
6: static SampleDPClass()
7: {
8: //注冊我們定義的依賴屬性Sample
9: SampleProperty = DependencyProperty.Register("Sample", typeof(string), typeof(SampleDPClass),
10: new PropertyMetadata("Knights Warrior!", OnValueChanged));
11: }
12:
13: private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
14: {
15: //當值改變時,我們可以在此做一些邏輯處理
16: }
17:
18: //屬性包裝器,通過它來讀取和設置我們剛才注冊的依賴屬性
19: public string Sample
20: {
21: get { return (string)GetValue(SampleProperty); }
22: set { SetValue(SampleProperty, value); }
23: }
24: }
五. 引入測試驅動開發
1,引入概念
由于本篇的依賴屬性體系是基于測試驅動開發完成的,所以我們就先來看一下什么叫測試驅動開發:測試驅動開發的基本思想就是在開發功能代碼之前,先編寫測試代碼。也就是說在明確要開發某個功能后,首先思考如何對這個功能進行測試,并完成測試代碼的編寫,然后編寫相關的代碼滿足這些測試用例。然后循環進行添加其他功能,直到完全部功能的開發。由于過程很長,在寫的時候也省略了不少步驟,所以有些地方銜接不是那么的流暢,對此表示非常的抱歉!
2,注意事項
根據自身做項目使用TDD的一點微薄經驗,總結了以下幾個注意事項:
◆ 找準切入點:
不論是開發一個新的系統還是復原系統,都必須先找準一個或多個切入點,從切入點經歷”測試代碼-功能代碼-測試-重構“來逐漸完善整個系統,往往這個切入點就是功能點,就是這個系統具備哪些功能,然后根據這些功能寫出測試用例。
◆ 測試列表:
大家都知道一個系統或者一個框架都是很龐大的,如果要引入測試驅動開發,首先我們必須要有一個測試列表,在任何階段想添加功能需求問題時,把相關功能點加到測試列表中,然后繼續開發的工作。然后不斷的完成對應的測試用例、功能代碼、重構。這樣可以避免疏漏的同時也能把控當前的進度。
◆ 測試驅動:
這個比較核心。完成某個功能,某個類,首先編寫測試代碼,考慮其如何使用、如何測試。然后在對其進行設計、編碼。這里也強調先編寫對功能代碼的判斷用的斷言語句,然后編寫相應的輔助語句。
◆ 良好的代碼設計及可測性:
功能代碼設計、開發時應該具有較強的可測試性。應該盡量保持良好的設計原則和代碼規范,如盡量依賴于接口、盡量高內聚、低耦合等等。
◆ 模塊或功能隔離:
不同代碼的測試應該相互隔離。對一塊代碼的測試只考慮此代碼的測試,不要考慮其實現細節,不然就會陷入一團亂麻之中,這個可以通過MOCK來實現,同時在開始的時候也要劃分好邊界。
◆ 適當引入MOCK:
在適當情況下引入MOCK來完成單元測試,這種情況尤其是在邊際交互比較多的案例當中,對于交互比較多且復雜的多個類關系可以用MOCK暫時模擬,這是一個不錯的解決方案。
◆ 由小到大、由偏到全、統籌兼顧:
一個產品或者一個項目是比較大的,所以我們這里就需要遵循由小到大、由偏到全、統籌兼顧的原則,分解功能和代碼。把所有的規模大、復雜性高的工作,分解成小的任務來完成,這樣既方便團隊協作,同時也減輕了復雜度,使整個開發一下子變得簡單了許多。
◆ 保持隨時重構的習慣:
很多開發者在經過測試代碼-功能代碼-測試通過以后就當完成了任務,其實你會發現隨著其他功能的引入或者使用過程中發現了很多重復、冗余的代碼、再或者先前的代碼結構和設計不太合理,這個時候就需要隨時的進行重構和單元測試,在一方面可以避免產生風險,另一方面可以使系統更加完善。
◆ 隨時進行回歸:
在”測試代碼-功能代碼-測試-重構“的循環中一定要記住多回歸,因為這樣可以保證當前的代碼是不是會影響到前面的功能,其實只需要看看紅綠燈就行。
◆ 查看和統計代碼覆蓋率:
通過前面的步驟之后,我們就要看一下實現的功能是否達到我們的預期目標,除了功能完善之外,還要保證代碼的覆蓋率,因為它是一個系統穩定與否、可維護性與否的一個重大標志。
3,工具介入
以后寫關于TDD的文章可能比較多,同時也都會用到這個工具,所以我們今天對它也稍帶介紹一下,正所謂“工欲善其事,必先利其器”。根據官方文檔解釋:TestDriven.NET是Visual Studio的一個TDD插件,最近版本是TestDriven.NET-3.0.2749 RTM版。其中一些新特性有:支持MSTest、.NET Reflector 6 Pro、VS 2010、Silverlight 4、NUnit 2.5.3,使用項目所用的.NET框架等。 下載地址:http://www.testdriven.NET/
這個工具使用起來比VS自帶的單元測試和測試覆蓋功能好用,所以從2008年開始基本就用它作為一個必備的工具使用。關于它具體的功能和怎么使用,我們這里不詳細介紹,網上也有很多文章,大家可以做一下參考和研究。下圖是安裝后以插件的形式出現在VS中的效果:
A,基本介紹
TestDriven.NET原來叫做NUnitAddIn,它是個Visual Studio插件,集成了如下測試框架:NUnit、MbUnit、 ZaneBug、MSTest、NCover、NCoverExplorer、Reflector、TypeMock、dotTrace和MSBee,它主要面向使用TDD的開發者,主要特性列舉如下:
◆ 單鍵運行方法、類、命名空間、項目和解決方案中的單元測試
◆ 能夠快速測試實例方法、靜態方法或屬性
◆ 可以直接跳到.NET Reflector中的任何方法、類型、項目或引用中,這個功能提供了相當大的方便
◆ 在調試過程中可以查看.NET Reflector中的任何模塊或堆棧信息
◆ 支持多種單元測試框架,包括NUnit、MbUnit、xUnit和MSTest
◆ 測試運行在自己的進程中以消除其他問題和邊際效應
◆ 可以輕松對任何目標測試進行調試或執行代碼覆蓋率測試(比微軟自帶的單元測試和代碼覆蓋功能要好用多了)
◆ 支持所有主流的.NET語言:C#、VB、C++和F#
B,TestDriven.NET 3.0中的新特性:
◆ TestDriven.NET是基于.NET框架的。再由于VS 2010支持使用多個.NET版本,所以支持各個VS版本和工具就沒有問題了
◆ 完全支持在VS 2008和VS 2010中使用MSTest
◆ 完全支持.NET Reflector 6 Pro
◆ 支持NUnit 2.5.3
◆ 支持和兼容VS 2005、VS 2008、VS 2010幾個版本
◆ 支持Silverlight 4的測試
C,兼容性
TestDriven.NET兼容于如下VS版本:Windows XP、Vista、Windows 7、Windows 2000、Windows 2003和Windows 2008(32和64位)上的Visual Studio 2005、2008和2010。官方已經不再對VS 2003支持。
D,版本
◆ 企業版:每臺機器一個許可認證
◆ 專業版:一般的許可形式
◆ 個人版:面向學生、開源開發者和試驗用戶的免費許可(大家可以下載這個版本,個人感覺很好用)
4,關于本篇
本篇文章沒有明確的寫作意圖,只是最近在深入研究MONO源碼時有感而發,當然作者本人也只是起到了一個研究者或者剖析者的角色。首先實現最簡單且基本的DependencyProperty.Register功能,然后再實現DependencyObject的GetValue和SetValue,接著實現PropertyMetadata的DefaultValue、PropertyChangedCallback、CoerceValueCallback等功能,然后完善DependencyProperty.Register注冊時添加ValidateValueCallback、RegisterAttached、RegisterAttachedReadOnly、RegisterReadOnly、OverrideMetadata、GetMetadata和AddOwner等相關功能。既然有了這些功能,自然就需要完善PropertyMetadata的IsSealed、Merge和OnApply等相關底層操作。當然在中間還需要DependencyObject的ClearValue、CoerceValue、GetLocalValueEnumerator、ReadLocalValue以及其他的Helper類,這里就不一一進行說明。對于邊際交互比較多且關聯比較大的操作,采用了Mock進行暫時模擬,在開發完了以后再進行了替換。在開發過程中,隨時進行單元測試和覆蓋率的檢查,這樣可以方便查看哪些功能還有問題以及整體的進度和質量的監控。
六. DependencyProperty測試代碼
在寫DependencyProperty測試代碼之前,我們先看一下它到底有哪些成員和方法,如下圖:
了解了上面DependencyProperty的基本功能,我們首先創建一個繼承自DependencyObject的類ObjectPoker,由于DependencyObject還沒有被創建,所以我們這里就先創建它,然后在ObjectPoker類里面實現我們的經典語句DependencyProperty.Register,由于Register有很多重載,為了方便TDD,就從最簡單的開始(三個參數,不牽涉到元數據類),然后再創建一個ObjectPoker的子類,這是方便后面測試DependencyProperty的相關功能。
1: class ObjectPoker : DependencyObject
2: {
3: //注冊依賴屬性property1
4: public static readonly DependencyProperty TestProp1 = DependencyProperty.Register("property1", typeof(string), typeof(ObjectPoker));
5: }
6:
7: class SubclassPoker : ObjectPoker
8: {
9: }
九. 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<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: }
NET技術:依賴屬性之“風云再起”,轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。