1. 程式人生 > >WPF依賴屬性(續)(3)依賴屬性儲存

WPF依賴屬性(續)(3)依賴屬性儲存

原文: 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, ValidateValueCallback 
validateValueCallback) { 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的數量
image

依賴屬性賦值與取值

在賦值與取值之前必須瞭解下情況
(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);
    }
}
輸出結果:
image

多屬性值(屬性百寶箱)

WPF具有強大的繫結功能,當初學WPF時,往往會把對屬性的賦值與繫結混淆了,對於這個繫結功能也是非常的陌生,是如何實現的,我們且不討論這一議題.來看看維護這份多屬性值的資料結構EffectiveValueEntry,測試以下程式碼

people.SetValue(DPCustomPeople.AgeProperty, 25);
people.SetValue(DPCustomPeople.NameProperty, "Clingingboy");
我們將在DispatcherObject內部看到,一個變數名為_effectiveValues的陣列
image

這就意味著對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的方法

image

這篇先到這裡,這部分還未完