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


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

时间: 2024-10-03 21:11:15

WPF中DependencyObject与DependencyProperty的源码简单剖析的相关文章

Android中AsyncTask基本用法与源码分析(API 23)

原文链接 http://sparkyuan.github.io/2016/03/23/AsyncTask源码剖析(API 23)/ 转载请注明出处 Android的UI是线程不安全的,想在子线程中更新UI就必须使用Android的异步操作机制,直接在主线程中更新UI会导致程序崩溃. Android的异步操作主要有两种,AsyncTask和Handler.AsyncTask是一个轻量的异步类,简单.可控.本文主要结合API 23的源码讲解一下AsyncTask到底是什么. 基本用法 声明:Andr

【141029】VC游戏编写中的求解最短路径算法源码

VC游戏编写中的求解最短路径算法源码,本示例是自动寻径演示,篮点是起点,红点是终点,按确定键开始.源码爱好者注:编译后运行的时候请把EXE文件从Debug目录中拷贝到项目根目录中,若不然会出错. 编著.程序设计:唐明理 程序顺序: 初始化队列.待处理节点入队列, 依靠对目的地估价距离插入排序,将离目的地估计最近的方案出队列,释放栈顶节点,释放申请过的所有节点,估价函数,估价 x,y 到目的地的距离,估计值必须保证比实际值小, 尝试下一步移动到 x,y 可行否,如果曾经有更好的方案移动到 (x,y

caffe中HingeLossLayer层原理以及源码分析

输入: bottom[0]: NxKx1x1维,N为样本个数,K为类别数.是预测值. bottom[1]: Nx1x1x1维, N为样本个数,类别为K时,每个元素的取值范围为[0,1,2,-,K-1].是groundTruth. 输出: top[0]: 1x1x1x1维, 求得是hingeLoss. 关于HingeLoss: p: 范数,默认是L1范数,可以在配置中设置为L1或者L2范数. :指示函数,如果第n个样本的真实label为k,则为,否则为-1. tnk: bottom[0]中第n个样

Android 中View的绘制机制源码分析 三

到目前为止,measure过程已经讲解完了,今天开始我们就来学习layout过程,不过在学习layout过程之前,大家有没有发现我换了编辑器,哈哈,终于下定决心从Html编辑器切换为markdown编辑器,这里之所以使用"下定决心"这个词,是因为毕竟Html编辑器使用好几年了,很多习惯都已经养成了,要改变多年的习惯确实不易,相信这也是还有很多人坚持使用Html编辑器的原因.这也反应了一个现象,当人对某一事物非常熟悉时,一旦出现了新的事物想取代老的事物时,人们都有一种抵触的情绪,做技术的

美!视差滚动在图片滑块中的应用【附源码下载】

视差滚动(Parallax Scrolling)已经被广泛应用于网页设计中,这种技术能够让原本平面的网页界面产生动感的立体效果.下面分享的这个图片滑块效果是难得一见的结合视差滚动的例子,之前的文章给大家分享过,这里再推荐一下.美女很养眼吧 :) 温馨提示:为保证最佳的效果,请在 IE10+.Chrome.Firefox 和 Safari 等现代浏览器中浏览. 您可能感兴趣的相关文章 太赞了!超炫的页面切换动画效果[附源码下载] 创意无限!一组网页边栏过渡动画[附源码下载] 好东西!动感的页面加载

OpenSSL 中 RSA 加密解密实现源码分析

1.RSA 公钥和私钥的组成,以及加密和解密的公式: 2.模指数运算: 先做指数运算,再做模运算,如 5^3 mod 7 = 125 mod 7 = 6 3.RSA加密算法流程: 选择一对不同的.并且足够大的素数 p 和 q 计算 n = p * q 计算欧拉函数 f(n) = (p-1) * (q-1),p 和 q 需要保密 寻找与 f(n) 互质的数 e,并且 1 < e < f(n) 计算 d,使得 d * e ≡ 1 mod f(n) 公钥 KU = (e , n)   私钥 KR =

Android 中View的绘制机制源码分析 二

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/46842891 本篇文章接着上篇文章的内容来继续讨论View的绘制机制,上篇文章中我们主要讲解了View的measure过程,今天我们就来学习ViewGroup的measure过程,由于ViewGroup只是一个抽象类,所以我们需要以一个具体的布局来分析measure过程,正如我上篇文章说的,我打算使用LinearLayout为例讲解measure过程,如果你还没有读过上篇文章,那么建议你先

Scala深入浅出实战经典《第83讲:Scala中List的实现内幕源码揭秘》笔记

终于从暑假回到正常的日子了~虽然开始学习<scala深入浅出实战经典>这门课程有些晚了,但是不管如何既然开始了,坚持下去才是重点.下面言归正传. 第83讲:Scala中List的实现内幕源码揭秘 Goal:List实现关键源码分析 Gains: 1)List中的诸多方法其实还是采用ListBuffer来实现的,而后ListBuffer利用toList返回不可变的List. 2)采用ListBuffer,效率较高:而toList方法返回List,也只是耗常量时间,并不会很大程度地影响性能. Q:

php将图片保存到mysql数据库及从数据库中读取图片的方法源码 转

php将图片保存到mysql数据库及从数据库中读取图片的方法源码 分类: 网站 2012-03-11 15:25 5059人阅读 评论(0) 收藏 举报 数据库mysqlphpsql serverquerydatabase 一般来讲都是把图片保存到服务器下,然后根据路径读出的,但是有时候出于安全及版权什么的考虑,会把图片保存到mysql的数据库中,然后再读出来,这样的图片点击右键属性,是看不到图片地址的.下面逍遥一生就介绍下如何用php把图片存储到mysql中及如何读出.     MySQL数据