这里,不讨论强制值回调和属性更改,有关内容参见msdn;这里着重讨论验证回调。
一、定义依赖属性,初始化属性元数据的默认值defaultValue为0.0,代码取自mdsn并作了一点小的改动public class Gauge : DependencyObject
{
public static readonly DependencyProperty CurrentReadingProperty = DependencyProperty.Register(
"CurrentReading",
typeof(double),
typeof(Gauge),
new FrameworkPropertyMetadata(0.0);
new ValidateValueCallback(IsValidReading)
);
public Gauge(int i)
{
Console.WriteLine(i);
}
public double CurrentReading
{
get { return (double)GetValue(CurrentReadingProperty); }
set { SetValue(CurrentReadingProperty, value); }
}
public static bool IsValidReading(object value)
{
Console.WriteLine("IsValidReading");
Double v = (Double)value;
return (!v.Equals(Double.NegativeInfinity) && !v.Equals(Double.PositiveInfinity));
}
}
private void buttonSunny_Click_1(object sender, RoutedEventArgs e)
{
Gauge g = new Gauge(1); //仅仅实例化了一个Gauge对象
}
输出:
IsValidReading 0.0
IsValidReading 0.0
Ctor
其中,回调的方法执行了两次,问题来了,回调方法为什么会执行两次呢?
下面是摘自msdn上的一段文字:
也就是说,在给属性元数据的defaultValue赋值、调用SetValue设置依赖项属性的本地值的时候,会执行验证回调;那么在什么地方又执行了回调方法呢?往下看
//new FrameworkPropertyMetadata(0.0)
new FrameworkPropertyMetadata()
输出:
IsValidReading 0.0
Ctor
在没有初始化defaultValue的情况下,defaultValue的默认值为0.0(double类型的默认值),此时回调方法只被调用了一次。如此看来,是不是在FrameworkPropertyMetadata的构造函数内部也调用了验证方法,即如果设置了defaultValue,构造函数就会调用验证方法;如果没有设置defaultValue,构造函数就不会调用验证方法。是这样吗?继续往下看
三、初始化依赖属性元数据对象为null,其他代码不变
//new FrameworkPropertyMetadata()
null
输出:
IsValidReading 0.0
Ctor
设置了FrameworkPropertyMetadata为空对象,就不存在构造函数,也就不会调用验证方法了。
另外,FrameworkPropertyMetadata有两个只有一个参数的构造函数:
public FrameworkPropertyMetadata(object defaultValue);
public FrameworkPropertyMetadata(PropertyChangedCallback propertyChangedCallback);
那么,当使用new FrameworkPropertyMetadata(null)来实例化一个对象的时候,调用的是哪个构造函数呢?继续把上面的代码作一点小小的改动
//null
new FrameworkPropertyMetadata(null)
然后看一下IL代码的.cctor()部分:
.method private hidebysig specialname rtspecialname static
void .cctor() cil managed
{
// 代码大小 54 (0x36)
.maxstack 8
IL_0000: ldstr "CurrentReading"
IL_0005: ldtoken [mscorlib]System.Double
IL_000a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000f: ldtoken Gauge
IL_0014: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0019: ldnull
IL_001a: newobj instance void [PresentationFramework]System.Windows.FrameworkPropertyMetadata::.ctor(class [WindowsBase]System.Windows.PropertyChangedCallback)
IL_001f: ldnull
IL_0020: ldftn bool Gauge::IsValidReading(object)
IL_0026: newobj instance void [WindowsBase]System.Windows.ValidateValueCallback::.ctor(object,
native int)
IL_002b: call class [WindowsBase]System.Windows.DependencyProperty [WindowsBase]System.Windows.DependencyProperty::Register(string,
class [mscorlib]System.Type,
class [mscorlib]System.Type,
class [WindowsBase]System.Windows.PropertyMetadata,
class [WindowsBase]System.Windows.ValidateValueCallback)
IL_0030: stsfld class [WindowsBase]System.Windows.DependencyProperty Gauge::CurrentReadingProperty
IL_0035: ret
} // end of method Gauge::.cctor
从IL_0019行到IL_001a行可以看出,new FrameworkPropertyMetadata(null)调用的是带有属性更改回调的构造函数。