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