寫在之前:
依賴屬性算是WPF醉醉基礎的一個組成了。平時寫代碼的時候,簡單的綁定很輕松,但是遇到復雜的層次比較多的綁定,真的是要命。所以,我覺得深刻認識依賴屬性是很有必要的。本篇只是個人學習的記錄,學習的博客是周永恆先生的《一站式WPF--依賴屬性(DependencyProperty)》,這算是一個系列了,說的很詳細。如果需要更好的學習,建議移步上述原文,受益匪淺。
什么是依賴屬性?
Windows Presentation Foundation (WPF) 提供了一組服務,這些服務可用於擴展公共語言運行時 (CLR) 屬性的功能,這些服務通常統稱為 WPF 屬性系統。由 WPF 屬性系統支持的屬性稱為依賴項屬性。----MSDN
也就是說,WPF提供一組叫做‘WPF屬性系統’的服務,而依賴屬性就是被這個服務所支持的屬性。我只能說,每個字都認識,但是放在一起認識的就不那么清晰了……
首先,想要了解它,必須知道它是為了什么而來。
雖然不清楚依賴屬性,但是屬性我們是很清楚的,封裝類的字段,表示類的狀態,編譯后被轉化為get_,set_方法,可以被類或結構等使用,常見的一個屬性如下:
public class ClassObject { private string name; public string Name { get { return name; } set { name = value; } } }
既然已經有了屬性,為什么還要有依賴屬性呢?必然是屬性有一些缺點了,而依賴屬性恰好能夠解決這個問題。以Button為例:
每次繼承,父類的私有字段都被繼承下來。對Button來說,大多數屬性並沒有被修改,仍然保持父類定義時的默認值。通常情況下,在整個Button的對象生命周期中,也只有少部分屬性被修改。也已看得出來:
- 每次繼承,子類對象都會獲得更多的屬性,這樣,繼承樹的低端對象不可避免的膨脹;
- 既然大多數屬性沒有被修改,那么就可以把這些屬性從對象中剝離,減少對象的體積;
可以知道,依賴屬性就是為了解決這個問題誕生了。首先定義依賴屬性,它里面存儲之前2中希望剝離的屬性:
public class DependencyProperty { // 維護了一個全局的Map用來儲存所有的DP internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>(); internal string Name;//注冊屬性名稱 internal object Value;//屬性值 internal object HashCode;//Mape唯一鍵值 private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue) { this.Name = name; this.Value = defaultValue; this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode(); } // 對外暴露一個Register方法用來注冊新的DP public static DependencyProperty Register(string name, Type propertyName, Type ownerType, object defaultValue) { DependencyProperty dp = new DependencyProperty(name, propertyName, ownerType, defaultValue); RegisteredDps.Add(dp.HashCode, dp); return dp; } }
然后定義DependencyObject來使用DP:
public class DependencyObject { // 注冊一個新的DP:NameProperty public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty); public object GetValue(DependencyProperty dp) { return DependencyProperty.RegisteredDps[dp.HashCode].Value; } public void SetValue(DependencyProperty dp, object value) { DependencyProperty.RegisteredDps[dp.HashCode].Value = value; } public string Name { get { return (string)GetValue(NameProperty); } set { SetValue(NameProperty, value); } } }
DependencyObject和文章開篇的ClassObject中的Name有什么不同呢?
>>DependencyObject.Name的實際值不是用字段保存在DependencyObject中,而是保存在NameProperty中,通過SetValue和GetValue來金星賦值取值操作。
在上述例子中,所有DependncuObject的對象將共用一個NameProperty,這在現實中是不實際的:只要修改一個對象的屬性,相當於所有對象的屬性值都被修改了。所以,修改的屬性,還是要維護在相應的對象中的:(修改部分用☆表示)
public class DependencyProperty { private static int globalIndex = 0;// ☆ // 維護了一個全局的Map用來儲存所有的DP internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>(); internal string Name;//注冊屬性名稱 internal object Value;//屬性值 internal int Index;// ☆
internal object HashCode;//Mape唯一鍵值 private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue) { this.Name = name; this.Value = defaultValue; this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode(); } // 對外暴露一個Register方法用來注冊新的DP public static DependencyProperty Register(string name, Type propertyName, Type ownerType, object defaultValue) { DependencyProperty dp = new DependencyProperty(name, propertyName, ownerType, defaultValue); globalIndex++;// ☆ dp.Index = globalIndex; // ☆ RegisteredDps.Add(dp.HashCode, dp); return dp; } }
所有修改過的DP都保存在EffectiveValueEntry里,這樣,就可以只保存修改的屬性,未修改過的屬性仍然讀取DP的默認值,優化了屬性的儲存。
public class DependencyObject { // 引入有效的概念// ☆ private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>(); // 注冊一個新的DP:NameProperty public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty); public object GetValue(DependencyProperty dp) { // ☆ EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault(i => i.PropertyIndex == dp.Index); if (effectiveValue.PropertyIndex != 0) { return effectiveValue.Value; } else { return DependencyProperty.RegisteredDps[dp.HashCode].Value;//僅此部分相同 } } public void SetValue(DependencyProperty dp, object value) { // 全部 ☆ EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault(i => i.PropertyIndex == dp.Index); if (effectiveValue.PropertyIndex != 0) { effectiveValue.Value = value; } else { effectiveValue = new EffectiveValueEntry() { PropertyIndex = dp.Index, Value = value }; } //DependencyProperty.RegisteredDps[dp.HashCode].Value = value; } public string Name { get { return (string)GetValue(NameProperty); } set { SetValue(NameProperty, value); } } }
internal struct EffectiveValueEntry { internal int PropertyIndex{get;set;} internal object Value{get;set;} }