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