关于依赖属性的验证回调

前端之家收集整理的这篇文章主要介绍了关于依赖属性的验证回调前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

这里,不讨论强制值回调和属性更改,有关内容参见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上的一段文字

如果提供的值对属性有效,回调将返回 true;否则,回调返回 false。 假定按照向属性系统注册的类型,属性的类型是正确的,因此通常不会在回调内执行类型检查。 属性系统可在多种不同操作中使用回调。 其中包括按照默认值进行初始类型初始化、通过调用 SetValue 进行编程更改或尝试使用提供的新默认值重写元数据。 如果验证回调是通过其中任何一种操作调用的,并且返回 false,则将会引发异常。 应用程序编写器必须准备处理这些异常。 验证回调常用于验证枚举值,或在属性设置的度量必须大于或等于零时约束整数值或双精度型值。

也就是说,在给属性元数据的defaultValue赋值、调用SetValue设置依赖项属性的本地值的时候,会执行验证回调;那么在什么地方又执行了回调方法呢?往下看

二、不初始化属性元数据的默认值defaultValue,其他代码不变
//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 [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)调用的是带有属性更改回调的构造函数

猜你在找的设计模式相关文章