在定义任何类型的属性时,都需要面对错误设置属性的可能性。对于传统的.NET属性,可尝试在属性设置器中捕获这类问题。但对于依赖项属性而言,这种方法不合适,因为可能通过WPF属性系统使用SetValue()方法直接设置属性。
作为代替,WPF提供了两种方法来阻止非法值:
- ValidateValueCallback:该回调函数可接受或拒绝新值。通常,该回调函数用于捕获违反属性约束的明显错误。可作为DependencyProperty.Register()方法的一个参数提供该回调函数。
- CoerceValueCallback:该回调函数可将新值修改为更能被接受的值。该回调函数通常用于处理为相同对象设置的依赖项属性值相互冲突的问题。这些值本身可能是合法的,但当同时应用时它们是不相容的。为了使用这个回调函数,当创建FrameworkPropertyMetadata对象时(然后该对象将被传递到DependencyProperty.Register()方法),作为构造函数的一个参数提供该回调函数。
下面是当应用程序试图设置依赖项属性时,所有这些内容的作用过程:
(1)首先,CoerceValueCallback方法有机会修改提供的值(通常,使提供的值和其他属性相容),或者返回DependencyProperty.UnsetValue,这会完全拒绝修改。
(2)接下来激活ValidateValueCallback方法。该方法返回true以接受一个值作为合法值,或者返回false拒绝值。与CoerceValueCallback方法不同,ValidateValueCallback方法不能访问设置属性的实际对象,这意味着你不能检查其他属性值。
(3)最后,如果前两个阶段都获得成功,就会触发PropertyChangedCallback方法。此时,如果希望为其他类提供通知,可以引发更改事件。
一、验证回调
正如前面所看到的,DependencyProperty.Register()方法接受可选的验证回调函数:
static FrameworkElement(){ FrameworkPropertyMetadata metadata=new FrameworkPropertyMetadata(new Thickness(),FrameworkPropertyMetadataOptions.AffectsMeasure); MarginProperty=DependencyProperty.Register("Margin",typeof(Thickness),typeof(FrameworkElement),metadata,new ValidateValueCallback(FrameworkElement.IsMarginValid)); .... }
可使用这个回调函数验证,验证通常应被添加到属性过程的设置部分。提供的回调函数必须指向一个接受对象参数并返回Boolean值得方法。返回true以接受对象是合法的,返回false拒绝对象。
对FrameworkElement.Margin属性的验证十分枯燥乏味,因为它依赖于内部的Thickness.IsValid()方法。该方法确保当前使用的Thickness对象(表示边距)是合法的。例如,可能构造了一个完全可以接受的Thickness对象(却不适于设置边距)。一个例子是Thickness对象使用了负值。如果提供的Thickness对象对于边距时是不合法的。IsMarginValid方法将返回false:
public static bool IsMarginValid(object value) { Thickness thickness1=(Thickness)value; return thickness1.IsValid(true,false,true,false); }
对于验证回调函数有一个限制:它们必须是静态方法而且无权访问正在被验证的对象。所有能够获得的信息只有刚刚应用的数值。尽管这样更便于重用属性,但可能无法创建考虑其他属性的验证例程。典型的例子是具有Maximum和Minimum属性的元素。显然,为Maximum属性设置的值不能小于为Minimum属性设置的值。但是,不能使用验证回调函数来实施这一逻辑,因为一次之恩你访问一个属性。
二、强制回调
通过FrameworkPropertyMetadata对象使用CoerceValueCallback回调函数。下面是示例:
FrameworkPropertyMetadata metadata=new FrameworkPropertyMetadata(new Thickness(),FrameworkPropertyMetadataOptions.AffectsMeasure); metadata.CoerceValueCallback=new CoerceValueCallback(CoerceMaximum); DependencyProperty.Register("Maxium",typeof(double),typeof(RangeBase),metadata);
可以通过CoerceValueCallback回调函数处理相互关联的属性。例如,ScrollBar控件提供了Maximum、Minimum和Value属性,这些属性都继承自RangeBase类。保持对这些属性进行调整的一种方法是使用属性强制。
例如,当设置Maximum属性时,必须使用强制以确保不能小于Minimum属性的值:
private static object CoerceMaximum(DependencyObject d,object value) { RangeBase base1=(RangeBase)d; if((double)value)<base1.Minimum) { return base1.Minimum; } return value; }
换句话说,如果应用于Maximum属性的值小于Minimum属性的值,就用Minimum属性的值设置Maximum属性。注意,CoerceValueCallback传递两个参数——准备使用的数值和该数值将要应用到得对象。
当设置Value属性时,会发生类似的强制过程。对Value属性进行强制,确保不会超出由Minimum和Maximum属性定义的范围,使用下面的代码:
internal static object ConstrainToRange(DependencyObject d,object value) { double newValue=(double)value; RangeBase base1=(RangeBase)d; double minimum=base1.Minimum; if(newValue<minimum) { return minimum; } double maximum=bas1.Maximum; if(newValue>maximum) { return maximum; } return newValue; }
Minimum属性根本不使用值强制。相反,一旦值发生变化,就触发PropertyChangedCallback,然后通过手动触发Maximum和Value属性的强制过程,使它们适应Minimum属性值得变化:
private static void OnMinimumChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) { RangeBase base1=(RangeBase)d; ... base1.CoerceValue(RangeBase.MaximumProperty); base1.CoerceValue(RangeBase.ValueProperty); }
类似地,一旦设置或强制Maximum属性的值,那么也会手动强制Value属性以适应Maximum属性值得变化:
private static void OnMaximumChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) { RangeBase base1=(RangeBase)d; ... base1.CoerceValue(RangeBase.ValueProperty); base1.OnMaximumChanged((double)e.OldValue,(double)e.NewValue); }
如果设置的值相互冲突,最终结果是Minimum属性具有优先权,其次是Maximum属性(并且可能会被Minimum属性强制),最后是Value属性(并且可能会被Maximum和Minimum属性强制)。
原文地址:https://www.cnblogs.com/Peter-Luo/p/12232496.html