WPF依賴屬性(續)(3)依賴屬性儲存
在之前的兩篇,很多朋友參與了討論,也說明各位對WPF/SL計數的熱情,對DP系統各抒已見,當然也出現了一些分歧. 以下簡稱DP為依賴屬性
總結下上文:
- 討論了DP的記憶體問題
- 討論了依賴屬性與附加屬性的區別
下面我們繼續討論DP的儲存.
儲存依賴屬性
(1)確保DP的唯一性
所有的DP由一個內部靜態的雜湊表(PropertyFromName)維護,一個物件定義的DP屬性鍵值不可以重複,相同鍵值的DP可以定義在其他物件中,為確保屬性唯一性,使用DP屬性鍵值和物件的HashCode組成,內部定義了一個FromNameKey物件,如下
private class FromNameKey { // Fields private int _hashCode; private string _name; private Type _ownerType; // Methods public FromNameKey(string name, Type ownerType) { this._name = name; this._ownerType = ownerType; this._hashCode = this._name.GetHashCode() ^ this._ownerType.GetHashCode(); } public override int GetHashCode() { return this._hashCode; } }
以下為內部精簡程式碼,忽略其他部分
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallbackvalidateValueCallback) { FromNameKey key = new FromNameKey(name, ownerType); if (PropertyFromName.Contains(key)) { throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", new object[] { name, ownerType.Name })); } DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback); PropertyFromName[key] = dp; return dp; }
(2)何時註冊DP
由於DP是以靜態方法註冊的,所以當擁有此DP的物件初始化後則會註冊該物件DP,我們來看下,當一個Window窗體擁有一些簡單的元素初始化後,其DP的數量
依賴屬性賦值與取值
在賦值與取值之前必須瞭解下情況
(1)DP不直接參與自身的存值與取值操作,而是由擁有DP的物件(依賴項物件,且稱為DP物件)完成,該物件從DispatcherObject派生.使用GetValue和SetValue方法
(2)DP物件對DP進行賦值前,必須先索引DP,用內部鍵值索引太麻煩,則為內部加了一個Index索引值,可以用DependencyProperty .GlobalIndex拿到這個值,DP列表資料結構內部維護著一個列表可以根據Index進行索引,這樣便於DP物件查詢DP
維護本地依賴屬性值
什麼是本地DP值,即是你修改過的DP屬性的值,而非採用預設的DP屬性元資料中的值在之前介紹過,每個DP都擁有的一個預設值,現在必須要把DP與擁有該DP的物件聯絡起來.
DP物件內部維護著一份本地DP值列表,當DP有所修改,那麼該DP會被記錄下來,儲存到內部的一個列表中.如下程式碼
var people = new DPCustomPeople(); people.SetValue(DPCustomPeople.AgeProperty, 0);AgeProperty的值將會被儲存起來.
若要獲取DP物件的本地DP值,DependencyObject公開了一個GetLocalValueEnumerator方法,可以獲取該列表,
也可以使用ReadLocalValue方法讀取一個DP的本地值
注意:GetValue方法如果本地值為空則返回預設值,但ReadLocalValue則會返回DependencyProperty.UnsetValue
如下測試程式碼
static void Main(string[] args) { var people = new DPCustomPeople(); Console.WriteLine("Before SetValue"); PrintLoaclValue(people); Console.WriteLine(); people.SetValue(DPCustomPeople.AgeProperty, 0); Console.WriteLine("After SetValue"); PrintLoaclValue(people); } static void PrintLoaclValue(DependencyObject obj) { var enumerator = obj.GetLocalValueEnumerator(); while (enumerator.MoveNext()) { Console.WriteLine("Property:" + enumerator.Current.Property + "/Value:" + enumerator.Current.Value); } }輸出結果:
多屬性值(屬性百寶箱)
WPF具有強大的繫結功能,當初學WPF時,往往會把對屬性的賦值與繫結混淆了,對於這個繫結功能也是非常的陌生,是如何實現的,我們且不討論這一議題.來看看維護這份多屬性值的資料結構EffectiveValueEntry,測試以下程式碼
people.SetValue(DPCustomPeople.AgeProperty, 25); people.SetValue(DPCustomPeople.NameProperty, "Clingingboy");我們將在DispatcherObject內部看到,一個變數名為_effectiveValues的陣列
這就意味著對DP的修改要此_effectiveValues關聯起來,那麼DP的GlobaIndex就起到了作用
注意:DP一旦建立,GlobaIndex就固定了,但每個DP物件的_effectiveValues則是動態建立的,所以在賦值與取值的時候,要將其關聯起來,內部採用了LookupEntry方法,根據DP的索引值,去列表中找到索引,並返回索引結果
EntryIndex entryIndex = this.LookupEntry(dp.GlobalIndex);
struct EntryIndex { private uint _store; public EntryIndex(uint index); public EntryIndex(uint index, bool found); public bool Found { get; } public uint Index { get; } }
同時其內部還具備多個操作EffectiveValueEntry的方法
這篇先到這裡,這部分還未完