• WPF中DependencyObject与DependencyProperty的源代码简单剖析


    Windbg调试WPF的依赖属性中提到了wpf的DependencyObject中DependencyProperty是怎样调试查看的。

    从中我们看出DO(DependencyObject)与 DP(DependencyProperty)一些内部实现。


    这篇文章我们就从源代码入手, 让大家了解下依赖对象中依赖属性的值的获取和赋值。

    我们先看个DP注冊的样例:
    public class MyStateControl : ButtonBase
    {
      public MyStateControl() : base() { }
      public Boolean State
      {
        get { return (Boolean)this.GetValue(StateProperty); }
        set { this.SetValue(StateProperty, value); } 
      }
      public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
        "State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));
    }
    


    上述Code中MyStateControl是DO。StateProperty是DP

    1.
    当MyStateControl进行初始化, 首先会运行StateProperty, 由于它是静态字段。从而运行DependencyProperty.Register方法。

    2.
    这种方法内部调用了DP的构造方法。 Code例如以下:
      // Create property
                DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
    3.
    DP的构造方法例如以下:
      private DependencyProperty( string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
            {
                _name = name;
                _propertyType = propertyType;
                _ownerType = ownerType;
                _defaultMetadata = defaultMetadata;
                _validateValueCallback = validateValueCallback;

                Flags packedData;
                lock (Synchronized)
                {
                    packedData = (Flags) GetUniqueGlobalIndex(ownerType, name);

                    RegisteredPropertyList.Add( this);
                }

                if (propertyType.IsValueType)
                {
                    packedData |= Flags.IsValueType;
                }

                if (propertyType == typeof (object))
                {
                    packedData |= Flags.IsObjectType;
                }

                if (typeof (Freezable).IsAssignableFrom(propertyType))
                {
                    packedData |= Flags.IsFreezableType;
                }

                if (propertyType == typeof (string))
                {
                    packedData |= Flags.IsStringType;
                }

                _packedData = packedData;
            }

    4.
    第3点的Code中我们能够看到packedData初始值是 (Flags) GetUniqueGlobalIndex(ownerType, name);
    GetUniqueGlobalIndex事实上是DP的私有静态变量GlobalIndexCount++得到的。


    下来这段代码能够看出:
      [Flags]
            private enum Flags : int
            {
                GlobalIndexMask                           = 0x0000FFFF,
                IsValueType                               = 0x00010000,
                IsFreezableType                           = 0x00020000,
                IsStringType                              = 0x00040000,
                IsPotentiallyInherited                    = 0x00080000,
                IsDefaultValueChanged                     = 0x00100000,
                IsPotentiallyUsingDefaultValueFactory     = 0x00200000,
                IsObjectType                              = 0x00400000,
                // 0xFF800000   free bits
            }

     public int GlobalIndex
            {
                get { return (int) (_packedData & Flags.GlobalIndexMask); }
            }
    _packedData的低4位即代表了StateProperty在整个DP数组RegisteredPropertyList中的索引。


    5.
    我们在构造里看到_packedData成员变量, 还记得我们“windbg怎样调试依赖属性”用到了它吗?
    我们用 .formats 命令转换去掉_packedData高位得到了DP在DO中的存储索引。
    通过第4和第5点, 想必大家已经对DP注冊有了了解


    接下来我们再看下DO中怎样获取DP值。以及怎样设置DP值。

    6.
    首先我们说下DO设置DP,Code相似:
    set { this.SetValue(StateProperty, value); } 
    
    能够看到我们通过DO的SetValue来给DP设置值。
    
    7.
    SetValue内部实现例如以下:
      private void SetValueCommon(
                DependencyProperty  dp,
                object              value,
                PropertyMetadata    metadata,
                bool                coerceWithDeferredReference,
                bool                coerceWithCurrentValue,
                OperationType       operationType,
                bool                isInternal)
            {
               。

    。。。。。

                EntryIndex entryIndex = LookupEntry(dp.GlobalIndex);
              。。

    。。。

    大家能够看到DO依据DP的GlobalIndex在_effectiveValues数组中查找到EntryIndex, EntryIndex包括相应index和Value。假设没有查到则在_effectiveValues中插入并返回index。

    (有兴趣能够看看LookupEntry的实现)
    在数组中找到之后接下来就是往数组中赋值。代码相似(真实比以下更复杂):
     if (entryIndex.Found)
                    {
                        newEntry = _effectiveValues[entryIndex.Index];
                    }
                   
    然后调用UpdateEffectiveValue发送属性更改通知。

    (还有其它一些Coerce相关代码,暂且不述)

     // fire change notification
                        NotifyPropertyChange(
                                new DependencyPropertyChangedEventArgs(
                                        dp,
                                        metadata,
                                        isAValueChange,
                                        oldEntry,
                                        newEntry,
                                        operationType));
    通过上面我们能够了解依赖对象中的依赖属性的赋值实现, 我们接下来再看看取值。

    8.
    DO获取DP的值,Code相似:
    get { return (Boolean)this.GetValue(StateProperty); }
    

    9. 
    GetValue内部实现例如以下:
       public object GetValue(DependencyProperty dp)
            {
               。。。

                // Call Forwarded
                return GetValueEntry(
                        LookupEntry(dp.GlobalIndex),
                        dp,
                        null,
                        RequestFlags.FullyResolved).Value;
            }

    能够看出是先找到DP的索引。然后接下来从_effectiveValues数组中找到相应的值。

    代码相似例如以下:


     entry = _effectiveValues[entryIndex.Index];

    (当然当中也有一些值优先级的处理,从来获取到正确的值)



    OK, 我们再回忆下Windbg中查看依赖对象的依赖属性的值的步骤, 1.得到依赖对象的值;2.得到依赖属性的值;3得到依赖属性的GlobalIndex。4.依据GlobalIndex去依赖对象中的_effectiveValues找到相应index的值。
    是不是对DP和DO的一些实现更了解了呢?
    
    

  • 相关阅读:
    Spring框架(一)-----核心理解
    vi常用编辑
    Avue使用renren-fast-vue开源脚手架工程(一)
    sqlServer触发器调用JavaWeb接口
    Linux常用别名设置
    油猴+IDM不限速下载
    Nginx配置静态web项目
    消息中间件rabbitMQ
    springboot自定义starter
    Nginx配置微信小程序 文件验证
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7353051.html
Copyright © 2020-2023  润新知