<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      一站式WPF--依賴屬性(DependencyProperty)一

      Windows Presentation Foundation (WPF) 提供了一組服務,這些服務可用于擴展公共語言運行時 (CLR) 屬性的功能,這些服務通常統稱為 WPF 屬性系統。由 WPF 屬性系統支持的屬性稱為依賴項屬性。

        這段是MSDN上對依賴屬性(DependencyProperty)的描述。主要介紹了兩個方面,WPF中提供了可用于擴展CLR屬性的服務;被這個服務支持的屬性稱為依賴屬性。

        單看描述,云里霧里的,了解一個知識,首先要知道它產生的背景和為什么要有它,那么WPF引入依賴屬性是為了解決什么問題呢?

      從屬性說起

         屬性是我們很熟悉的,封裝類的字段,表示類的狀態,編譯后被轉化為get_,set_方法,可以被類或結構等使用。 一個常見的屬性如下:

         1: public class NormalObject
         2: {
         3:     private string _unUsedField;
         4:  
         5:     private string _name;
         6:     public string Name
         7:     {
         8:         get
         9:         {
        10:             return _name;
        11:         }
        12:         set
        13:         {
        14:             _name = value;
        15:         }
        16:     }   
        17: }

        在面向對象的世界里,屬性大量存在,比如Button,就大約定義了70-80個屬性來描述其狀態。那么屬性的不足又在哪里呢?

        當然,所謂的不足,要針對具體環境來說。拿Button來講,它的繼承樹是Button->ButtonBase->ContentControl->Control->FrameworkElement->UIElement->Visual->DependencyObject->…

        每次繼承,父類的私有字段都被繼承下來。當然,這個繼承是有意思的,不過以Button來說,大多數屬性并沒有被修改,仍然保持著父類定義時的默認值。通常情況,在整個Button對象的生命周期里,也只有少部分屬性被修改,大多數屬性一直保持著初始值。每個字段,都需要占用4K等不等的內存,這里,就出現了期望可以優化的地方:

      • 因繼承而帶來的對象膨脹。每次繼承,父類的字段都被繼承,這樣,繼承樹的低端對象不可避免的膨脹。
      • 大多數字段并沒有被修改,一直保持著構造時的默認值,可否把這些字段從對象中剝離開來,減少對象的體積。

      依賴屬性的原型

        根據前面提出的需求,依賴屬性就應運而生了。一個簡單的依賴屬性的原型如下:

      DependencyProperty:

         1: public class DependencyProperty
         2: {
         3:     internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>();
         4:     internal string Name;
         5:     internal object Value;
         6:     internal object HashCode;
         7:  
         8:     private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue)
         9:     {
        10:         this.Name = name;
        11:         this.Value = defaultValue;
        12:         this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
        13:     }
        14:  
        15:     public static DependencyProperty Register(string name, Type propertyType, Type ownerType, object defaultValue)
        16:     {
        17:         DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultValue);
        18:         RegisteredDps.Add(dp.HashCode, dp);
        19:         return dp;
        20:     }
        21: }
        22:  

      DependencyObject:

         1: public class DependencyObject
         2: {
         3:     private string _unUsedField;
         4:  
         5:     public static readonly DependencyProperty NameProperty = 
         6: DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty);
         7:  
         8:     public object GetValue(DependencyProperty dp)
         9:     {
        10:         return DependencyProperty.RegisteredDps[dp.HashCode].Value;
        11:     }
        12:  
        13:     public void SetValue(DependencyProperty dp, object value)
        14:     {
        15:         DependencyProperty.RegisteredDps[dp.HashCode].Value = value;
        16:     }
        17:  
        18:     public string Name
        19:     {
        20:         get
        21:         {
        22:             return (string)GetValue(NameProperty);
        23:         }
        24:         set
        25:         {
        26:             SetValue(NameProperty, value);
        27:         }
        28:     }
        29: }
        30:  

        這里,首先定義了依賴屬性DependencyProperty,它里面存儲前面我們提到希望抽出來的字段。DP內部維護了一個全局的Map用來儲存所有的DP,對外暴露了一個Register方法用來注冊新的DP。當然,為了保證在Map中鍵值唯一,注冊時需要根據傳入的名字和注冊類的的HashCode取異或來生成Key。這里最關鍵的就是最后一個參數,設置了這個DP的默認值。

        然后定義了DependencyObject來使用DP。首先使用DependencyProperty.Register方法注冊了一個新的DP(NameProperty),然后提供了GetValue和SetValue兩個方法來操作DP。最后,類似前面例子中的NormalObject,同樣定義了一個屬性Name,和NormalObject的區別是,實際的值不是用字段來保存在DependencyObject中的,而是保存在NameProperty這個DP中,通過GetValue和SetValue來完成屬性的賦值取值操作。

        當然,作為一個例子,為了簡潔,很多情況沒有考慮,現在來測試一下是否解決了前面的問題。

        新建兩個對象,NormalObject和DependencyObject,在VS下打開SOS查看:

      .load sos
      extension C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded
      !DumpHeap -stat -type NormalObject
      total 1 objects
      Statistics:
      MT    Count    TotalSize Class Name
      009e30d0        1           16 DPDemonstration.NormalObject
      Total 1 objects
      !DumpHeap -stat -type DependencyObject
      total 1 objects
      Statistics:
      MT    Count    TotalSize Class Name
      009e31a0        1           12 DPDemonstration.DependencyObject
      Total 1 objects

        這里在對象中分別建立了一個_unUsedField的字段,.Net的GC要求對象的最小Size為12字節。如果對象的Size不足12字節,則會自動補齊。默認的Object對象占用8字節,Syncblk(4字節)以及TypeHandle(4字節),為了演示方便,加入了一個_unUsedField(4字節)來補齊。

        這里,DependencyObject相比NormalObject,減少了_name的儲存空間4字節。

      再進一步

         萬里長征第一步,這個想法可以解決我們希望的問題,這個做法還不能讓人接受。在這個實現中,所有DependencyObject共用一個DP,這個可以理解,但修改一個對象的屬性后,所有對象的屬性相當于都被修改了,這個就太可笑了。

        所以對象屬性一旦被修改,這個還是要維護在自己當中的,修改一下前面的DependencyObject,引入一個有效(Effective)的概念。

      改進的DependencyObject,加入了_effectiveValues:

         1: public class DependencyObject
         2: {
         3:     private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>();
         4:  
         5:     public static readonly DependencyProperty NameProperty = 
         6: DependencyProperty.Register("Name", typeof(string), typeof(DependencyObject), string.Empty);
         7:  
         8:     public object GetValue(DependencyProperty dp)
         9:     {
        10:         EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
        11:         if (effectiveValue.PropertyIndex != 0)
        12:         {
        13:             return effectiveValue.Value;
        14:         }
        15:         else
        16:         {
        17:             return DependencyProperty.RegisteredDps[dp.HashCode].Value;
        18:         }
        19:     }
        20:  
        21:     public void SetValue(DependencyProperty dp, object value)
        22:     {
        23:         EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
        24:         if (effectiveValue.PropertyIndex != 0)
        25:         {
        26:             effectiveValue.Value = value;
        27:         }
        28:         else
        29:         {
        30:             effectiveValue = new EffectiveValueEntry() { PropertyIndex = dp.Index, Value = value };
        31:             _effectiveValues.Add(effectiveValue);
        32:         }
        33:     }
        34:  
        35:     public string Name
        36:     {
        37:         get
        38:         {
        39:             return (string)GetValue(NameProperty);
        40:         }
        41:         set
        42:         {
        43:             SetValue(NameProperty, value);
        44:         }
        45:     }
        46: }

      新引進的EffectiveValueEntry:

         1: internal struct EffectiveValueEntry
         2: {
         3:     internal int PropertyIndex { get; set; }
         4:  
         5:     internal object Value { get; set; }
         6: }

       改進的DependencyProperty,加入了ProperyIndex:

         1: public class DependencyProperty
         2: {
         3:     private static int globalIndex = 0;
         4:     internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>();
         5:     internal string Name;
         6:     internal object Value;
         7:     internal int Index;
         8:     internal object HashCode;
         9:  
        10:     private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue)
        11:     {
        12:         this.Name = name;
        13:         this.Value = defaultValue;
        14:         this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
        15:     }
        16:  
        17:     public static DependencyProperty Register(string name, Type propertyType, Type ownerType, object defaultValue)
        18:     {
        19:         DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultValue);
        20:         globalIndex++;
        21:         dp.Index = globalIndex;
        22:         RegisteredDps.Add(dp.HashCode, dp);
        23:         return dp;
        24:     }
        25: }

        在DependencyObject加入了一個_effectiveValues,就是把所有修改過的DP都保存在EffectiveValueEntry里,這樣,就可以達到只保存修改的屬性,未修改過的屬性仍然讀取DP的默認值,優化了屬性的儲存。

      更進一步的發展

         到目前為止,從屬性到依賴屬性的改造一切順利。但隨著實際的使用,又一個問題暴露出來了。使用繼承,子類可以重寫父類的字段,換句話說,這個默認值應該是可以子類化的。那么怎么處理,子類重新注冊一個DP,傳入新的默認值?

        當然,不會實現的這么丑陋。同一個DP,要想支持不同的默認值,那么內部就要維護一個對應不同DependencyObjectType的一個List,可以根據傳入的DependencyObject的類型來讀取它對應的默認值。

        DP內需要維護一個自描述的List,按照微軟的命名規則,添加新的類型屬性元數據(PropertyMetadata):

         1: public class PropertyMetadata
         2: {
         3:     public Type Type { get; set; }
         4:     public object Value { get; set; }
         5:  
         6:     public PropertyMetadata(object defaultValue)
         7:     {
         8:         this.Value = defaultValue;
         9:     }
        10: }

      對應修改DependencyProperty

         1: public class DependencyProperty
         2: {
         3:     private static int globalIndex = 0;
         4:     internal static Dictionary<object, DependencyProperty> RegisteredDps = new Dictionary<object, DependencyProperty>();
         5:     internal string Name;
         6:     internal object Value;
         7:     internal int Index;
         8:     internal object HashCode;
         9:     private List<PropertyMetadata> _metadataMap = new List<PropertyMetadata>();
        10:     private PropertyMetadata _defaultMetadata;
        11:  
        12:     private DependencyProperty(string name, Type propertyName, Type ownerType, object defaultValue)
        13:     {
        14:         this.Name = name;
        15:         this.Value = defaultValue;
        16:         this.HashCode = name.GetHashCode() ^ ownerType.GetHashCode();
        17:  
        18:         PropertyMetadata metadata = new PropertyMetadata(defaultValue) { Type = ownerType };
        19:         _metadataMap.Add(metadata);
        20:         _defaultMetadata = metadata;
        21:     }
        22:  
        23:     public static DependencyProperty Register(string name, Type propertyType, Type ownerType, object defaultValue)
        24:     {
        25:         DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultValue);
        26:         globalIndex++;
        27:         dp.Index = globalIndex;
        28:         RegisteredDps.Add(dp.HashCode, dp);
        29:         return dp;
        30:     }
        31:  
        32:     public void OverrideMetadata(Type forType, PropertyMetadata metadata)
        33:     {
        34:         metadata.Type = forType;
        35:         _metadataMap.Add(metadata);
        36:     }
        37:  
        38:     public PropertyMetadata GetMetadata(Type type)
        39:     {
        40:         PropertyMetadata medatata = _metadataMap.FirstOrDefault((i) => i.Type == type) ??
        41:             _metadataMap.FirstOrDefault((i) => type.IsSubclassOf(i.Type));
        42:         if (medatata == null)
        43:         {
        44:             medatata = _defaultMetadata;
        45:         }
        46:         return medatata;
        47:     }
        48: }

      修改DenpendencyObject中的GetValue并更改_effectiveValues,為了簡潔去掉了NameProperty以及SetValue.

         1: public class DependencyObject
         2: {
         3:    private List<EffectiveValueEntry> _effectiveValues = new List<EffectiveValueEntry>();
         4:  
         5:    public object GetValue(DependencyProperty dp)
         6:    {
         7:        EffectiveValueEntry effectiveValue = _effectiveValues.FirstOrDefault((i) => i.PropertyIndex == dp.Index);
         8:        if (effectiveValue.PropertyIndex != 0)
         9:        {
        10:            return effectiveValue.Value;
        11:        }
        12:        else
        13:        {
        14:            PropertyMetadata metadata;
        15:            metadata = DependencyProperty.RegisteredDps[dp.HashCode].GetMetadata(this.GetType());
        16:            return metadata.Value;
        17:        }
        18:    }
        19: }

      這樣,就可以定義一個SubDependencyObject,調用OverrideMedata向DP的_metadataMap中加入新的Metadata。

         1: public class SubDependencyObject : DependencyObject
         2: {
         3:     static SubDependencyObject()
         4:     {
         5:         NameProperty.OverrideMetadata(typeof(SubDependencyObject), new PropertyMetadata("SubName"));
         6:     }
         7: }

        創建一個DependencyObject以及SubDependencyObject,可以發現,Name的值已經被改為”SubName”了。當然,實際DP中對Metadata的操作比較繁瑣,當子類調用OverrideMetadata時會涉及到Merge操作,把新的Metadata與父類的合二為一。并且在GetMetadata中,要取得自己或者是與它最近的父類的Metadata,為了可以獲得最近的父類,WPF引入了一個DependencyObjectType的類,在構造時傳入BaseType=this.base.GetType(),這里為了簡單,忽略不計。

      WPF對依賴屬性的擴展

         前面的例子里,依據優化儲存的思想,我們打造了一個DependencyProperty。當然,有了這樣一門利器,不好好打磨打磨真是對不起它,WPF在這個基礎上對DP進行了擴展,使其更加的強大。

        對通常的CLR屬性來說,在Set中加入一些邏輯判斷是很正常的,當然也可以在Set中發出一些事件或者更改其他一些屬性。那么依賴屬性,它對此又有什么支持呢?

        順水推舟,WPF在DP的PropertyMedata中加入了PropertyChangedCallback以及CoerceValueCallback等。這些Delegate可以在構造PropertyMetadata時傳入,在SetValue過程中,會取得對應的PropertyMetadata,然后回調PropertyChangedCallback。這個PropertyMetadata可以在構建DP時傳入,也可以在子類調用OverrideMetadata時傳入,這就保證了同一個DP不同的DependencyObject可以有不同的應用。WPF對此進行了很多擴展,定義了一套屬性賦值的規則,包括計算(calculate)、限制(Coerce)、驗證(Validate)等等。

        當然,這些擴展說開了會很多,WPF對此也進行了精巧的設計,這也就是我們開篇提到的WPF提供了一組服務,用于擴展CLR屬性。

      多屬性值

         發展都是由需求來推動的,在WPF的實現過程中,又產生了這樣一個需要:

        WPF是原生支持動畫的,一個DP屬性,比如Button的Width,你可以加入動畫使他在1秒內由100變為200,在動畫結束后,又希望它能恢復原來的屬性值。同理,你可以在XAML表達式中對屬性進行賦值,當表達式失效時同樣期望他恢復成原來的屬性值。這個需求來自于,對同一個屬性的賦值可能發生在不同的場合,當對象狀態改變時屬性也要發生相應的變化,這里就產生了兩個需要:

      • 屬性對外暴露一個值,但內部可以存放多個值,根據狀態(條件)的改變來確定當前值。
      • 這些狀態(條件)要定義優先級,根據優先級來判斷當前應取哪個值。

        同一個屬性有多個值,這個對CLR屬性來說有些難為它了。但是對DP來說卻很簡單,本來DP的值就是保存在我們定義的EffectiveValueEntry中的,以前是保存一個Value,現在定義多個值就可以了。

         1: internal struct EffectiveValueEntry
         2: {
         3:     private object _value;
         4:  
         5:     internal int PropertyIndex { get; set; }
         6:  
         7:     internal object Value 
         8:     {
         9:         get
        10:         {
        11:             return _value;
        12:         }
        13:         set
        14:         {
        15:             _value = value;
        16:         }
        17:     }
        18:  
        19:     internal ModifiedValue ModifiedValue 
        20:     {
        21:         get
        22:         {
        23:             if (this._value != null)
        24:             {
        25:                 return (this._value as ModifiedValue);
        26:             }
        27:             return null;
        28:         }
        29:     }
        30: }

      對應的ModifiedValue:

         1: internal class ModifiedValue
         2: {
         3:     internal object AnimatedValue { get; set; }
         4:     internal object BaseValue { get; set; }
         5:     internal object CoercedValue { get; set; }
         6:     internal object ExpressionValue { get; set; }
         7: }

        當屬性沒有被修改過,ModifiedValue為空,當修改過后,ModifiedValue被賦值。這里EffectiveValueEntry定義了很多方法如SetExpressionValue(object value), SetAnimatedValue(object value)等來向ModifiedValue中寫入對應值;并且EffectiveValueEntry提供了IsAnimated,IsExpression等屬性來表示當前的狀態。當然,這個賦值的操作比較復雜,這個優先級分兩大類:一 ModifiedValue中各屬性的優先級;二 對于ExpressionValue來說,它又有自己的優先級,Local>Style>Template…這里就不詳細解釋了。

      依賴屬性的優點

         回過頭來,總結一下依賴屬性的優點:

      • 優化了屬性的儲存,減少了不必要的內存使用。
      • 加入了屬性變化通知,限制、驗證等,
      • 可以儲存多個值,配合Expression以及Animation等,打造出更靈活的使用方式。

      總結

        借助于依賴屬性,WPF提供了強大的屬性系統,可以支持數據綁定、樣式、動畫、附加屬性等功能。這篇文章主要是簡略的實現了一個從屬性到依賴屬性的發展過程,當然,具體和WPF的實現還有偏差,希望朋友們都能抓住這個主要的脈絡,更好的去玩轉它。

        除了依賴屬性的實現,還有一些很重要的部分,比如借助于依賴屬性提出的附加屬性,以及如何利用依賴屬性來更好的設計實現程序,使用依賴屬性有哪些要注意的地方。呵呵,那就,下篇吧。


      示例程序下載

      作者:周永恒 
      出處:http://www.rzrgm.cn/Zhouyongh  
      本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

      posted @ 2009-09-10 17:16  周永恒  閱讀(31975)  評論(48)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产成人精品无色码| 乱码精品一区二区亚洲区| 最新精品露脸国产在线| 国产一区日韩二区三区| 四虎影视久久久免费| 一本色道婷婷久久欧美| 国产成人精品成人a在线观看| 天天综合亚洲色在线精品| 亚洲va在线∨a天堂va欧美va| 亚洲顶级裸体av片| 国产精品一区二区三区专区| 精品午夜福利在线视在亚洲| 一个人免费观看WWW在线视频| 真实国产老熟女无套中出| 成在人线av无码免费看网站直播| 办公室强奷漂亮少妇视频 | 精品人妻伦九区久久69| 日韩东京热一区二区三区| 狠狠色狠狠色五月激情| 久久精品亚洲精品国产色婷| 大地资源高清免费观看| 国产欧美va欧美va在线| 日韩精品亚洲国产成人av| 国产999久久高清免费观看| 亚洲精品动漫免费二区| 无码精品一区二区免费AV| 久久综合九色综合97婷婷| 台湾省| 在线观看国产成人av片| 亚洲国产精品久久久天堂麻豆宅男 | 日本韩国日韩少妇熟女少妇| 阿坝县| 国产成人亚洲综合91精品| 欧美人成精品网站播放| 日韩精品中文字幕无码一区| 人妻系列中文字幕精品| 久久亚洲精品情侣| 风韵丰满妇啪啪区老老熟女杏吧| 一 级做人爱全视频在线看| 日韩高清福利视频在线观看| 99久久99久久精品免费看蜜桃 |