这里,不讨论强制值回调和属性更改,有关内容参见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,构造函数就不会调用验证方法。是这样吗?继续往下看
//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 [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)调用的是带有属性更改回调的构造函数。