依賴屬性的定義,分爲3步(以PresentationFramework中的System.Windows.Controls.Button爲例)html
1. 聲明依賴屬性編程
public static readonly DependencyProperty IsDefaultProperty
2. 調用DependencyProperty.Register建立依賴屬性實例數組
IsDefaultProperty = DependencyProperty.Register("IsDefault", typeof(bool), typeof(Button), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, new PropertyChangedCallback(Button.OnIsDefaultChanged)));
此例中,第一個參數是依賴屬性名稱,第一個參數是依賴屬性的值類型,第三個參數爲依賴屬性所在的類型,第四個參數是可選的爲依賴屬性提供元數據。ide
3. 爲依賴屬性添加傳統的CLR屬性封裝函數
public bool IsDefault { get { return (bool) base.GetValue(IsDefaultProperty); } set { base.SetValue(IsDefaultProperty, BooleanBoxes.Box(value)); } }
爲何性能
1. 聲明依賴屬性時爲何是public、static和readonlythis
按照慣例全部的依賴屬性一般都是public, static而且以Property結尾。由於是public的因此須要使用readonly來防止第三方代碼對依賴屬性的意外修改。spa
2. DependencyProperty.Register的第一和第二個參數code
第一個參數和第二個參數用來唯一肯定一個依賴屬性,換句話說WPF爲每一個依賴屬性建立一個實例,該實例由依賴屬性名稱和其所在類型所決定,並由DependencyProperty.Register返回,能夠從DependencyProperty的反編譯代碼中獲得證明htm
private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback) { FromNameKey key = new FromNameKey(name, ownerType); lock (Synchronized) { if (PropertyFromName.Contains(key)) { throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", new object[] { name, ownerType.Name })); } }
你能夠在上面的代碼中看到PropertyFromName(第二行紅色),PropertyFromName是一個私有的靜態哈希表,用來存放使用DependencyProperty.Register註冊到WPF對象層次結構中的全部(包括貢獻依賴屬性的全部類)依賴屬性實例的靜態引用。從上面代碼能夠看出,當name(第一個參數,依賴屬性名稱)和ownerType(第二參數,貢獻依賴屬性的類)肯定時,唯一對應PropertyFromName中的一個值(即爲依賴對象實例靜態引用)。
3. DependencyProperty.Register的第四個參數
第四個參數包含描述依賴屬性的元數據,定製WPF處理依賴屬性的行爲,提供屬性值改變時的回調函數和屬性值的有效性驗證等。
4. 傳統.Net屬性封裝
這一步並非必須的, 應爲GetValue和SetValue(後面將說明)是publish的,因此在代碼中能夠直接調用這兩個函數(必須繼承DependencyObject)。可是提供該封裝能夠在編程時方便使用,若是要用XAML屬性中使用該依賴屬性就必定要提供該封裝。
它們是如何工做的
1. DependencyProperty.Register作了什麼
先看一下它的反編譯代碼:
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback) { RegisterParameterValidation(name, propertyType, ownerType); PropertyMetadata defaultMetadata = null; if ((typeMetadata != null) && typeMetadata.DefaultValueWasSet()) { defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue); } DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback); if (typeMetadata != null) { property.OverrideMetadata(ownerType, typeMetadata); } return property; }
它調用了RegisterCommon
private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback) { FromNameKey key = new FromNameKey(name, ownerType); lock (Synchronized) { if (PropertyFromName.Contains(key)) { throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", new object[] { name, ownerType.Name })); } } if (defaultMetadata == null) { defaultMetadata = AutoGeneratePropertyMetadata(propertyType, validateValueCallback, name, ownerType); } else { if (!defaultMetadata.DefaultValueWasSet()) { defaultMetadata.DefaultValue = AutoGenerateDefaultValue(propertyType); } ValidateMetadataDefaultValue(defaultMetadata, propertyType, name, validateValueCallback); } DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback); defaultMetadata.Seal(dp, null); if (defaultMetadata.IsInherited) { dp._packedData |= Flags.IsPotentiallyInherited; } if (defaultMetadata.UsingDefaultValueFactory) { dp._packedData |= Flags.IsPotentiallyUsingDefaultValueFactory; } lock (Synchronized) { PropertyFromName[key] = dp; } if (TraceDependencyProperty.IsEnabled) { TraceDependencyProperty.TraceActivityItem(TraceDependencyProperty.Register, dp, dp.OwnerType); } return dp; }
在RegisterCommon函數中第一行紅色代碼使用接收的依賴屬性名稱和所在類的名稱建立了FromNameKey實例key;第二行紅色代碼檢測key是否在PropertyFromName中,若是存在則拋異常(WPF只爲類的每一個依賴屬性建立一個實例);若是key不存在,也即類的某個唯一命名依賴屬性不存在,則第三行紅色代碼調用DependencyProperty的private構造函數爲該依賴屬性建立實例;最後第四行紅色代碼把建立的依賴屬性加入到PropertyFromName中。
2. DependencyProperty的私有構造函數
private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback) { Flags uniqueGlobalIndex; this._metadataMap = new InsertionSortMap(); this._name = name; this._propertyType = propertyType; this._ownerType = ownerType; this._defaultMetadata = defaultMetadata; this._validateValueCallback = validateValueCallback; lock (Synchronized) { uniqueGlobalIndex = (Flags) GetUniqueGlobalIndex(ownerType, name); RegisteredPropertyList.Add(this); } if (propertyType.IsValueType) { uniqueGlobalIndex |= Flags.IsValueType; } if (propertyType == typeof(object)) { uniqueGlobalIndex |= Flags.IsObjectType; } if (typeof(Freezable).IsAssignableFrom(propertyType)) { uniqueGlobalIndex |= Flags.IsFreezableType; } if (propertyType == typeof(string)) { uniqueGlobalIndex |= Flags.IsStringType; } this._packedData = uniqueGlobalIndex; }
internal static int GetUniqueGlobalIndex(Type ownerType, string name) { if (GlobalIndexCount < 0xffff) { return GlobalIndexCount++; } if (ownerType != null) { throw new InvalidOperationException(SR.Get("TooManyDependencyProperties", new object[] { ownerType.Name + "." + name })); } throw new InvalidOperationException(SR.Get("TooManyDependencyProperties", new object[] { "ConstantProperty" })); }
從上面兩段代碼能夠看出WPF爲每一個DependencyProperty實例建立了一個自增的索引uniqueGlobalIndex,並把該索引和DependencyProperty值類型(使用Flags枚舉來表示)一塊兒封裝在_packedData中。從上面代碼能夠看出WPF至多支持同時建立0xffff(65535)個依賴屬性。
3. DependencyObject
使用依賴屬性的全部類都必須繼承DependencyObject,該類定義了操做依賴屬性的相關方法,以下面介紹的SetValue和GetValue。DependencyObject具備一個私有的實例字段_effectiveValues用於存放依賴屬性值和對應的依賴屬性索引,換句話說繼承自DependencyObject的類的每一個實例均維護着用於存放該類定義的經過DependencyProperty.Register註冊到WPF基礎結構的依賴屬性值(注意不是依賴屬性實例)的數組。該數組的元素爲EffectiveValueEntry類型,包含依賴屬性實例索引(上面已經說明全部的類實例共享一個依賴屬性實例)和依賴屬性值(因類的實例的不一樣而不一樣)的對應關係。
依賴屬性和依賴屬性值的存儲方案以下圖:
3.1 SetValue
這是由DependencyObject提供的實例方法,用於設置DependencyProperty在類實例的值。調用SetValue時WPF建立EffectiveValueEntry實例用於存放依賴屬性值和依賴屬性實例索引的對象關係並插入到_effectiveValues數組中,依賴屬性值在_effectiveValues中是按照依賴屬性的索引從小到大有序存放的(詳細實現可查看DependencyObject類成員函數InsertEntry的反編譯代碼)。
3.2 GetValue
這是由DependencyObject提供的實例方法,用於獲取DependencyProperty在類實例的值。調用GetValue時WPF根據提供的依賴屬性實例索引在_effectiveValues中搜索對應的屬性值,因爲_effectiveValues是有序的,因此實現中使用二分法來提升搜索性能(詳細實現可查看DependencyObject類成員函數LookupEntry的反編譯代碼)。
總結
將依賴屬性從依賴屬性的值上剝離,主要是爲了性能上的考慮。一個WPF類可能使用幾十上百個字段,而且在一次窗體呈現中該類可能被實例化不僅一次(如一個Button包含有96個字段,且在一個窗體中可能包含不少個Button),若是使用傳統CLR屬性方式,則將爲附加到字段實例上的本地化數據分配存儲空間。假設控件每一個字段的本地化數據大小平均爲m,包含的字段數爲f,控件被建立的次數爲n,則須要的總空間爲M = m * f * n,消耗的空間是直線上升的。使用依賴屬性,因爲依賴屬性實例引用是靜態的,且WPF只爲依賴屬性建立一個實例,因此實際所須要的空間只剩下爲每一個控件實例保存依賴屬性值的空間(即M=0)。
引用
《Windows.Presentation.Foundation.Unleashed》
http://www.abhisheksur.com/2011/07/internals-of-dependency-property-in-wpf.html?_sm_au_=iVVjWL1ZNjHpkVpM