在.NET中有事件也有屬性,WPF中加入了路由事件,也加入了依賴屬性。最近在寫項目時還不知道WPF依賴屬性是乾什麼用的,在使用依賴項屬性的時候我都以為是在用.NET中的屬性,但是確實上不是的,通過閱讀文章和看WPF的書籍已經瞭解了WPF的依賴屬性的使用,我們今天就來看看為什麼WPF中要加入依賴屬性? ...
在.NET中有事件也有屬性,WPF中加入了路由事件,也加入了依賴屬性。最近在寫項目時還不知道WPF依賴屬性是乾什麼用的,在使用依賴項屬性的時候我都以為是在用.NET中的屬性,但是確實上不是的,通過閱讀文章和看WPF的書籍已經瞭解了WPF的依賴屬性的使用,我們今天就來看看為什麼WPF中要加入依賴屬性?
WPF中的依賴屬性有別於.NET中的屬性,因為在WPF中有幾個很重要的特征都是需要依賴項屬性的支持,例如數據綁定,動畫,樣式設置等。WPF絕大多數屬性都是依賴項屬性,只不過它是用了普通的.NET屬性過程進行了包裝,通過這種包裝,就可以像使用屬性一樣使用依賴項屬性了,在後面會說一下怎麼通過這種方式包裝的。這就使用了舊技術來包裝新技術的設計理念就不會幹擾.NET。WPF中的依賴屬性主要有以下三個優點:
1、依賴屬性加入了屬性變化通知、限制、驗證等功能。這樣可以使我們更方便地實現應用,同時大大減少了代碼量。
2、節約記憶體:在WinForm中,每個UI控制項的屬性都賦予了初始值,這樣每個相同的控制項在記憶體中都會保存一份初始值。而WPF依賴屬性很好地解決了這個問題,它內部實現使用哈希表存儲機制,對多個相同控制項的相同屬性的值都只保存一份。
3、支持多種提供對象:可以通過多種方式來設置依賴屬性的值。可以配合表達式、樣式和綁定來對依賴屬性設置值。
剛纔我們一直在說屬性,先來看看屬性是什麼吧。先創建一個類Person,裡面有name屬性。
上面就是創建好的屬性,看著是不是很簡單。屬性的創建就是這麼簡單,在我們想要使用這個類的地方初始化就能用。
既然說WPF中絕大多數的屬性都是依賴項屬性,我看了一下依賴屬性怎麼進行創建。
1、依賴屬性的所在類型繼承自DependencyObject類。
2、使用public static 聲明一個DependencyProperty的變數,該變數就是真正的依賴屬性。
3、類型的靜態構造函數中通過Register方法完成依賴屬性的元數據註冊。
4、提供依賴屬性的包裝屬性,通過這個屬性來完成對依賴屬性的讀寫操作。
Public class Person : DependencyObject
//CLR屬性包裝器,使得依賴屬性NameProperty在外部能夠像普通屬性那樣使用
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
//DependencyProperty.Register 參數說明
//第四個參數是具有附加屬性設置的FramWorkPropertyMetadata對象。
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(Person), new PropertyMetadata("DefaultName"));
從上面代碼可以看出,依賴屬性是通過調用DependencyObject的GetValue和SetValue來對依賴屬性進行讀寫的。它使用哈希表來進行存儲的,對應的Key就是屬性的HashCode值,而值(Value)則是註冊的DependencyPropery;而C#中的屬性是類私有欄位的封裝,可以通過對該欄位進行操作來對屬性進行讀寫。屬性是欄位的包裝,WPF中使用屬性對依賴屬性進行包裝。
WPF 屬性系統提供一種強大的方法,使得依賴屬性的值由多種因素決定,從而實現諸如實時屬性驗證、後期綁定以及向相關屬性發出有關其他屬性值發生更改的通知等功能。 用來確定依賴屬性值的確切順序和邏輯相當複雜。 瞭解此順序有助於避免不必要的屬性設置,並且還有可能澄清混淆,使你正確瞭解為何某些影響或預測依賴屬性值的嘗試最終卻沒有得出所期望的值。依賴屬性可以在多個位置“設置”,界面代碼如下:
本地屬性集在設置時具有最高優先順序,動畫值和強制除外。 如果在本地設置某個值,你可以期待該值優先得到應用,甚至期待其優先順序高於任何樣式或控制項模板。 在上面示例中,此處Background本地設置為紅色。 因此,即使它是隱式樣式,否則將會應用於該作用域中的該類型的所有元素,在此作用域中定義的樣式不是最高優先順序給予Background屬性及其值。 如果從該 Button 實例中刪除本地值紅色,樣式將獲得優先順序,而按鈕將從該樣式中獲得 Background 值。 在該樣式中,觸發器具有優先順序,因此當滑鼠位於按鈕上時,按鈕為藍色,其他情況下則為綠色。
下麵的圖是在網上找的依賴屬性優先順序列表圖,大家後面再使用屬性時可以留意一下優先順序。
依賴屬性的繼承是WPF屬性系統的一項功能。 屬性值繼承使元素樹中的子元素可以從父元素獲取特定屬性的值,並繼承該值,就如同它是在最近的父元素中任意位置設置的一樣。 父元素可能也已通過屬性值繼承獲得了其值,因此系統有可能一直遞歸到頁面根。 屬性值繼承不是預設屬性系統行為;屬性必須用特定的元數據設置來建立,以便使該屬性對子元素啟動屬性值繼承。
看到上面圖片你可能已經發現了問題:StatusBar沒有顯式設置FontSize值,但它的字體大小沒有繼承Window.FontSize的值,而是保持了系統的預設值。導致這樣的問題是因為並不是所有元素都支持屬性值繼承的,如StatusBar、Tooptip和Menu控制項。另外,StatusBar等控制項截獲了從父元素繼承來的屬性,並且該屬性也不會影響StatusBar控制項的子元素。例如,如果我們在StatusBar中添加一個Button。那麼這個Button的FontSize屬性也不會發生改變,其值為預設值。
如果想要依賴屬性繼承,我們可以進行自定義依賴屬性繼承屬性值。
xmlns:sys="clr-namespace:System;assembly=mscorlib"
在寫代碼是都會考慮可能發生的錯誤。在定義屬性時,也需要考慮錯誤設置屬性的可能性。對於傳統.NET屬性,可以在屬性的設置器中進行屬性值的驗證,不滿足條件的值可以拋出異常。但對於依賴屬性來說,這種方法不合適,因為依賴屬性通過SetValue方法來直接設置其值的。然而WPF有其代替的方式,WPF中提供了兩種方法來用於驗證依賴屬性的值。
1、ValidateValueCallback:該回調函數可以接受或拒絕新值。該值可作為DependencyProperty.Register方法的一個參數。
2、CoerceValueCallback:該回調函數可將新值強制修改為可被接受的值。例如某個依賴屬性工作年齡的值範圍是25到55,在該回調函數中,可以對設置的值進行強制修改,對於不滿足條件的值,強制修改為滿足條件的值。如當設置為負值時,可強制修改為0。該回調函數PropertyMetadata構造函數參數進行傳遞。