Inside dependency property
依賴(lài)屬性的定義,分為3步(以PresentationFramework中的System.Windows.Controls.Button為例)
1.? 聲明依賴(lài)屬性
public static readonly DependencyProperty IsDefaultProperty?
2. 調(diào)用DependencyProperty.Register創(chuàng)建依賴(lài)屬性實(shí)例
IsDefaultProperty = DependencyProperty.Register("IsDefault", typeof(bool), typeof(Button), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, new PropertyChangedCallback(Button.OnIsDefaultChanged)));?
此例中,第一個(gè)參數(shù)是依賴(lài)屬性名稱(chēng),第一個(gè)參數(shù)是依賴(lài)屬性的值類(lèi)型,第三個(gè)參數(shù)為依賴(lài)屬性所在的類(lèi)型,第四個(gè)參數(shù)是可選的為依賴(lài)屬性提供元數(shù)據(jù)。
3. 為依賴(lài)屬性添加傳統(tǒng)的CLR屬性封裝
public bool IsDefault{get{return (bool) base.GetValue(IsDefaultProperty);}set{base.SetValue(IsDefaultProperty, BooleanBoxes.Box(value));}}
?
為什么
1. 聲明依賴(lài)屬性時(shí)為什么是public、static和readonly
按照慣例所有的依賴(lài)屬性通常都是public, static并且以Property結(jié)尾。因?yàn)槭莗ublic的所以需要使用readonly來(lái)防止第三方代碼對(duì)依賴(lài)屬性的意外修改。
?
2. DependencyProperty.Register的第一和第二個(gè)參數(shù)
第一個(gè)參數(shù)和第二個(gè)參數(shù)用來(lái)惟一確定一個(gè)依賴(lài)屬性,換句話(huà)說(shuō)WPF為每個(gè)依賴(lài)屬性創(chuàng)建一個(gè)實(shí)例,該實(shí)例由依賴(lài)屬性名稱(chēng)和其所在類(lèi)型所決定,并由DependencyProperty.Register返回,可以從DependencyProperty的反編譯代碼中得到證實(shí)
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是一個(gè)私有的靜態(tài)哈希表,用來(lái)存放使用DependencyProperty.Register注冊(cè)到WPF對(duì)象層次結(jié)構(gòu)中的所有(包括貢獻(xiàn)依賴(lài)屬性的所有類(lèi))依賴(lài)屬性實(shí)例的靜態(tài)引用。從上面代碼可以看出,當(dāng)name(第一個(gè)參數(shù),依賴(lài)屬性名稱(chēng))和ownerType(第二參數(shù),貢獻(xiàn)依賴(lài)屬性的類(lèi))確定時(shí),惟一對(duì)應(yīng)PropertyFromName中的一個(gè)值(即為依賴(lài)對(duì)象實(shí)例靜態(tài)引用)。
?
3. DependencyProperty.Register的第四個(gè)參數(shù)
第四個(gè)參數(shù)包含描述依賴(lài)屬性的元數(shù)據(jù),定制WPF處理依賴(lài)屬性的行為,提供屬性值改變時(shí)的回調(diào)函數(shù)和屬性值的有效性驗(yàn)證等。
?
4. 傳統(tǒng).Net屬性封裝
這一步并不是必須的, 應(yīng)為GetValue和SetValue(后面將說(shuō)明)是publish的,所以在代碼中可以直接調(diào)用這兩個(gè)函數(shù)(必須繼承DependencyObject)。但是提供該封裝可以在編程時(shí)方便使用,如果要用XAML屬性中使用該依賴(lài)屬性就一定要提供該封裝。
?
它們是如何工作的
?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; }它調(diào)用了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函數(shù)中第一行紅色代碼使用接收的依賴(lài)屬性名稱(chēng)和所在類(lèi)的名稱(chēng)創(chuàng)建了FromNameKey實(shí)例key;第二行紅色代碼檢測(cè)key是否在PropertyFromName中,如果存在則拋異常(WPF只為類(lèi)的每個(gè)依賴(lài)屬性創(chuàng)建一個(gè)實(shí)例);如果key不存在,也即類(lèi)的某個(gè)惟一命名依賴(lài)屬性不存在,則第三行紅色代碼調(diào)用DependencyProperty的private構(gòu)造函數(shù)為該依賴(lài)屬性創(chuàng)建實(shí)例;最后第四行紅色代碼把創(chuàng)建的依賴(lài)屬性加入到PropertyFromName中。
2. DependencyProperty的私有構(gòu)造函數(shù)
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為每個(gè)DependencyProperty實(shí)例創(chuàng)建了一個(gè)自增的索引uniqueGlobalIndex,并把該索引和DependencyProperty值類(lèi)型(使用Flags枚舉來(lái)表示)一起封裝在_packedData中。從上面代碼可以看出WPF至多支持同時(shí)創(chuàng)建0xffff(65535)個(gè)依賴(lài)屬性。
3.? DependencyObject
使用依賴(lài)屬性的所有類(lèi)都必須繼承DependencyObject,該類(lèi)定義了操作依賴(lài)屬性的相關(guān)方法,如下面介紹的SetValue和GetValue。DependencyObject具有一個(gè)私有的實(shí)例字段_effectiveValues用于存放依賴(lài)屬性值和對(duì)應(yīng)的依賴(lài)屬性索引,換句話(huà)說(shuō)繼承自DependencyObject的類(lèi)的每個(gè)實(shí)例均維護(hù)著用于存放該類(lèi)定義的通過(guò)DependencyProperty.Register注冊(cè)到WPF基礎(chǔ)結(jié)構(gòu)的依賴(lài)屬性值(注意不是依賴(lài)屬性實(shí)例)的數(shù)組。該數(shù)組的元素為EffectiveValueEntry類(lèi)型,包含依賴(lài)屬性實(shí)例索引(上面已經(jīng)說(shuō)明所有的類(lèi)實(shí)例共享一個(gè)依賴(lài)屬性實(shí)例)和依賴(lài)屬性值(因類(lèi)的實(shí)例的不同而不同)的對(duì)應(yīng)關(guān)系。
依賴(lài)屬性和依賴(lài)屬性值的存儲(chǔ)方案如下圖:
3.1 SetValue
?這是由DependencyObject提供的實(shí)例方法,用于設(shè)置DependencyProperty在類(lèi)實(shí)例的值。調(diào)用SetValue時(shí)WPF創(chuàng)建EffectiveValueEntry實(shí)例用于存放依賴(lài)屬性值和依賴(lài)屬性實(shí)例索引的對(duì)象關(guān)系并插入到_effectiveValues數(shù)組中,依賴(lài)屬性值在_effectiveValues中是按照依賴(lài)屬性的索引從小到大有序存放的(詳細(xì)實(shí)現(xiàn)可查看DependencyObject類(lèi)成員函數(shù)InsertEntry的反編譯代碼)。
3.2 GetValue
?這是由DependencyObject提供的實(shí)例方法,用于獲取DependencyProperty在類(lèi)實(shí)例的值。調(diào)用GetValue時(shí)WPF根據(jù)提供的依賴(lài)屬性實(shí)例索引在_effectiveValues中搜索對(duì)應(yīng)的屬性值,由于_effectiveValues是有序的,所以實(shí)現(xiàn)中使用二分法來(lái)提高搜索性能(詳細(xì)實(shí)現(xiàn)可查看DependencyObject類(lèi)成員函數(shù)LookupEntry的反編譯代碼)。
?
總結(jié)
將依賴(lài)屬性從依賴(lài)屬性的值上剝離,主要是為了性能上的考慮。一個(gè)WPF類(lèi)可能使用幾十上百個(gè)字段,并且在一次窗體呈現(xiàn)中該類(lèi)可能被實(shí)例化不只一次(如一個(gè)Button包含有96個(gè)字段,且在一個(gè)窗體中可能包含很多個(gè)Button),如果使用傳統(tǒng)CLR屬性方式,則將為附加到字段實(shí)例上的本地化數(shù)據(jù)分配存儲(chǔ)空間。假設(shè)控件每個(gè)字段的本地化數(shù)據(jù)大小平均為m,包含的字段數(shù)為f,控件被創(chuàng)建的次數(shù)為n,則需要的總空間為M = m * f * n,消耗的空間是直線(xiàn)上升的。使用依賴(lài)屬性,由于依賴(lài)屬性實(shí)例引用是靜態(tài)的,且WPF只為依賴(lài)屬性創(chuàng)建一個(gè)實(shí)例,所以實(shí)際所需要的空間只剩下為每個(gè)控件實(shí)例保存依賴(lài)屬性值的空間(即M=0)。
?
?引用
《Windows.Presentation.Foundation.Unleashed》
http://www.abhisheksur.com/2011/07/internals-of-dependency-property-in-wpf.html?_sm_au_=iVVjWL1ZNjHpkVpM
轉(zhuǎn)載于:https://www.cnblogs.com/vicsmb/p/3438120.html
總結(jié)
以上是生活随笔為你收集整理的Inside dependency property的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 分布式流媒体直播服务器系统 For Li
- 下一篇: 水调歌头·中秋