我学习WPF使用的是《WPF编程宝典》(第四版)。之前读到自定义控件,也就是18.2颜色拾取器的时候,有一句话我深深的记在脑海里。
因为WPF不允许重新进行属性变化回调函数。
多么智能,多么贴心。为了避免有的人没有看过这本书,我把书里的例子贴在这里。
public class ColorPicker:System.Windows.Controls.UserControl
{
public static DependencyProperty RedProperty;
public static DependencyProperty GreenProperty;
public static DependencyProperty BlueProperty;
public static DependencyProperty ColorProperty;
static ColorPicker()
{
ColorProperty = DependencyProperty.Register("Color", typeof(Color),
typeof(ColorPicker), new FramewrokPropertyMetadata(Colors.Black, new PropertyChangedCallback(OnColorChanged));
RedProperty = DependencyProperty.Register("Red", typeof(byte),
typeof(ColorPicker), new FramewrokPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged));
GreenProperty = DependencyProperty.Register("Green", typeof(byte),
typeof(ColorPicker), new FramewrokPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged));
BlueProperty = DependencyProperty.Register("Blue", typeof(byte),
typeof(ColorPicker), new FramewrokPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged));
}
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public byte Red
{
get { return (byte)GetValue(RedProperty); }
set { SetValue(RedProperty, value); }
}
public byte Green
{
get { return (byte)GetValue(GreenProperty); }
set { SetValue(GreebProperty, value); }
}
public byte Blue
{
get { return (byte)GetValue(BlueProperty); }
set { SetValue(BlueProperty, value); }
}
private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Color newColor = (Color)e.NewValue;
Color oldColor = (Color)e.OldValue;
ColorPicker colorPicker = (ColorPicker)sender;
colorPicker.Red = newColor.R;
colorPicker.Green = newColor.G;
colorPikcer.Blue = newColor.B;
}
private static void OnColorRGBChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ColorPicker colorPicker = (ColorPicker)sender;
Color color = colorPicker.Color;
if (e.Property == RedProperty)
color.R = (byte)e.NewValue;
else if (e.Property == GreenProperty)
color.G = (byte)e.NewValue;
else if (e.Property == BlueProperty)
color.B = (byte)e.NewValue;
colorPicker.Color = color;
}
}
很明显,这段代码是希望在改变RGB的值时,能够更新Color。同时在更新Color时,能够更新RGB。并且,这样写并不会造成循环调用。
我一直坚信这是WPF内部进行优化,能够保证在任何情况下,callback中对依赖属性进行设置,不会发生循环调用的情况。
知道今天,我把对应的Color改成了TimeSpan,循环调用发生了。那一刻,我是怀疑人生的。
先给出结论:DependencyPropertyChangedCallback中并不会进行什么神奇的操作来规避循环调用。能够规避部分循环调用的原因时,SetValue会对值进行判定,如果值没有发生改变,则不会调用DependencyPropertyChangedCallback。
为什么改成TimeSpan类型会发生循环调用呢?因为如果把Second赋值一个超过60的数,会进位。感兴趣的朋友可以想想为什么。欢迎留言讨论。