码迷,mamicode.com
首页 > Windows程序 > 详细

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

时间:2015-08-01 15:44:03      阅读:239      评论:0      收藏:0      [点我收藏+]

标签:dependencyobject   dependencyproperty   effectivevalues   entryindex   wpf   

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的一些实现更了解了呢?


版权声明:本文为博主原创文章,欢迎转载,仅请署名

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

标签:dependencyobject   dependencyproperty   effectivevalues   entryindex   wpf   

原文地址:http://blog.csdn.net/muzizongheng/article/details/47186583

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!