您好,
会员登录 快速注册
退出 ( 条未读消息)
关于本站 意见反馈 首页

公告:小宅博客网可以开发票了,需要发票的,去群里找群主哈!!
全部文章分类
  • 人工智能 >

  • 编程语言 >

  • WPF系列 >

  • ASP.NET系列 >

  • Linux >

  • 数据库 >

  • 嵌入式 >

  • WEB技术 >

  • PLC系列 >

  • 微服务与框架 >

  • 小宅DIY >

  • 学习资料 >

OpenCv基础 ANN车牌识别 yolov5车牌识别 指针式仪表识别 ROS系列 YOLO Halcon Detectron2 昇腾AI ChatGPT在线体验 英伟达JETSON ChatGLM ChatTTS FunASR 地平线 ByteTrack 魔搭社区 LangChain
C C# C++ Python Java Go
WPF
ASP.NET小功能 GPS定位系统-MVC GPS定位系统-VUE ASP.NET WebRTC
Linux Linux内核 Shell MakeFile
MySql SqlServer Oracle
STM8 STM32 51单片机
VUE入门 HTML JavaScript CSS layui镜像网站 ElementUi中文官网 element-plus 图标
三菱 欧姆龙 西门子 施耐德 松下 台达
IOTSharp IOTGateway ABP FRAMEWORK Docker
亚克力音响 编程仙途:智驭万法
面试题与技巧 Python入门技能树 微软C#教程
首页 编程之美 工具下载 全国就业 流量地图 文心一言
WPF
内容介绍 1、新建WPF工程 2、Application介绍 3、Dispatcher介绍 4、Window 介绍 5、主要布局属性介绍 6、Grid网格布局 7、UniformGrid布局 8、DockPanel与ViewBox布局 9、Border与ViewBox布局 10、依赖属性(一) 11、依赖属性(二) 12、依赖属性(三) 13、依赖属性(四) 14、WPF中的数据绑定(一) 15、WPF中的数据绑定(二) 16、WPF中的数据绑定(三) 17、WPF中的数据绑定(四) 18、ListView示例(一) 19、ListView示例(二) 20、DataGrid示例(一) 21、DataGrid示例(二) 22、DataGrid示例(三) 23、引用FontAwesome矢量图 24、ListBox日志效果 25、Polygon绘制多边形 26、Ellipse绘制实心圆 27、数据模板DataTemplate 基于WPF的exe远程升级程序
12、依赖属性(三)
14、WPF中的数据绑定(一)
激萌の小宅 博客园 WPF

文章作者:激萌の小宅

促销:¥0

价格:¥0

配送方式: 购买后立即生效(如购买异常,请联系站长)
付款之后一定要等待自动跳转结束,否则购买可能会失败
  • 0 天

    有效期

  • 0

    总销量

  • 0

    累计评价

依赖属性(四) - (十三)


六、依赖属性回调、验证及强制值

       我们通过下面的这幅图,简单介绍一下WPF属性系统对依赖属性操作的基本步骤:


       借用一个常见的图例,介绍一下WPF属性系统对依赖属性操作的基本步骤: 

       第一步,确定Base Value,对同一个属性的赋值可能发生在很多地方。比如控件的背景(Background),可能在Style或者控件的构造函数中都对它进行了赋值,这个Base Value就要确定这些值中优先级最高的值,把它作为Base Value。

       第二步,估值。如果依赖属性值是计算表达式(Expression),比如说一个绑定,WPF属性系统就会计算表达式,把结果转化成一个实际值。

       第三步,动画。动画是一种优先级很高的特殊行为。如果当前属性正在作动画,那么因动画而产生的值会优于前面获得的值,这个也就是WPF中常说的动画优先。

       第四步,强制。如果我们在FrameworkPropertyMetadata中传入了 CoerceValueCallback委托,WPF属性系统会回调我们传入的的delagate,进行属性值的验证,验证属性值是否在我们允许的范围之内。例如强制设置该值必须大于于0小于10等等。在属性赋值过程中,Coerce拥有 最高的优先级,这个优先级要大于动画的优先级别。

       第五步,验证。验证是指我们注册依赖属性如果提供了ValidateValueCallback委托,那么最后WPF会调用我们传入的delegate,来验证数据的有效性。当数据无效时会抛出异常来通知。

       那么应该如何使用这些功能呢? 

       前面我们讲了基本的流程,下面我们就用一个小的例子来进行说明:

XAML的代码如下:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="400">
    <Grid>
        <StackPanel>
            <Button Name="btnDPTest" Click="btnDPTest_Click" >属性值执行顺序测试</Button>
        </StackPanel>
    </Grid>
</Window>


新增类SimpleDP.cs,如下:


MainWindow.xaml.cs代码如下:

using System.Windows;
using WpfApp.Models;

namespace WpfApp
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void btnDPTest_Click(object sender, RoutedEventArgs e)
        {
            SimpleDP test = new SimpleDP();
            test.ValidDP = 1;
        }
    }
}


SimpleDP.cs代码如下:

using System;
using System.Windows;

namespace WpfApp.Models
{
    public class SimpleDP : DependencyObject
    {
        public static readonly DependencyProperty ValidDPProperty =
            DependencyProperty.Register("ValidDP", typeof(int), typeof(SimpleDP),
                new FrameworkPropertyMetadata(0,
                    FrameworkPropertyMetadataOptions.None,
                    new PropertyChangedCallback(OnValueChanged),
                    new CoerceValueCallback(CoerceValue)),
                    new ValidateValueCallback(IsValidValue));

        public int ValidDP
        {
            get { return (int)GetValue(ValidDPProperty); }
            set { SetValue(ValidDPProperty, value); }
        }

        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Console.WriteLine("当属性值的OnValueChanged方法被调用,属性值为: {0}", e.NewValue);
        }

        private static object CoerceValue(DependencyObject d, object value)
        {
            Console.WriteLine("当属性值的CoerceValue方法被调用,属性值强制为: {0}", value);
            return value;
        }

        private static bool IsValidValue(object value)
        {
            Console.WriteLine("当属性值的IsValidValue方法被调用,对属性值进行验证,返回bool值,如果返回True表示严重通过,否则会以异常的形式抛出: {0}", value);
            return true;
        }
    }
}


执行结果如下:


【勘误】

       在博客园原文中对执行结果的描述不对,按站长的测试结果来看,当ValidDP属性变化之后,确实按照流程,先 Coerce后Validate的顺序执行了。


【原文如下】

       ValidDP属性变化之后,PropertyChangeCallback就会被调用。可以看到结果并没有完全按照我们先前的流程先 Coerce后Validate的顺序执行,有可能是WPF内部做了什么特殊处理,当属性被修改时,首先会调用Validate来判断传入的value是 否有效,如果无效就不继续后续的操作,这样可以更好的优化性能。从上面的结果上看出,CoerceValue后面并没有立即ValidateValue, 而是直接调用了PropertyChanged。这是因为前面已经验证过了value,如果在Coerce中没有改变value,那么就不用再验证了。如 果在 Coerce中改变了value,那么这里还会再次调用ValidateValue操作,和前面的流程图执行的顺序一样,在最后我们会调用 ValidateValue来进行最后的验证,这就保证最后的结果是我们希望的那样了。

       上面简单介绍了处理流程,下面我们就以一个案例来具体看一看上面的流程到底有没有出入。


XAML代码如下:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp.Controls"
        Title="MainWindow" Height="400" Width="500">
    <Grid>
        <StackPanel Orientation="Vertical">
            <local:MyValiDP x:Name="myValiDP1" MaxValue="500" MinValue="0" />
            <Label Content="可以设置最小值为0和最小大值为500" Height="30"/>
            <StackPanel Orientation="Horizontal" Height="60">
                <Label Content="当前值为 : "/>
                <Label Background="Yellow" BorderBrush="Black" BorderThickness="1"
                   IsEnabled="False" Content="{Binding ElementName=myValiDP1, Path=CurrentValue}" Height="25" VerticalAlignment="Top" />
            </StackPanel>

            <WrapPanel >
                <Label Content="最小值" />
                <Slider x:Name="sliderMin" Minimum="-200" Maximum="100" Width="300" ValueChanged="sliderMin_ValueChanged" SmallChange="10"  />
                <Label Content="{Binding ElementName=sliderMin, Path=Value}" />
            </WrapPanel>
            <WrapPanel >
                <Label Content="最大值" />
                <Slider x:Name="sliderMax" Minimum="200" Maximum="800" Width="300" ValueChanged="sliderMax_ValueChanged" SmallChange="10" />
                <Label Content="{Binding ElementName=sliderMax, Path=Value}" />
            </WrapPanel>
        </StackPanel>
    </Grid>
</Window>


MainWindow.xaml.cs代码如下:

using System.Windows;

namespace WpfApp
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //设置Current的值
            myValiDP1.CurrentValue = 100;
        }
        private void sliderMin_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            //设置Current的值
            myValiDP1.CurrentValue = (int)sliderMin.Value;
        }
        private void sliderMax_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {

            //设置Current的值
            myValiDP1.CurrentValue = (int)sliderMax.Value;

        }
    }
}


依赖属性代码MyValiDP.cs文件如下:

using System;
using System.Windows;

namespace WpfApp.Controls
{
    class MyValiDP : System.Windows.Controls.Control
    {
        //注册Current依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
        public static readonly DependencyProperty CurrentValueProperty = DependencyProperty.Register(
            "CurrentValue",
            typeof(double),
            typeof(MyValiDP),
            new FrameworkPropertyMetadata(
                Double.NaN,
                FrameworkPropertyMetadataOptions.None,
                new PropertyChangedCallback(OnCurrentValueChanged),
                new CoerceValueCallback(CoerceCurrentValue)
            ),
            new ValidateValueCallback(IsValidValue)
        );

        //属性包装器,通过它来暴露Current的值
        public double CurrentValue
        {
            get { return (double)GetValue(CurrentValueProperty); }
            set { SetValue(CurrentValueProperty, value); }
        }

        //注册Min依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
        public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(
        "MinValue",
        typeof(double),
        typeof(MyValiDP),
        new FrameworkPropertyMetadata(
            double.NaN,
            FrameworkPropertyMetadataOptions.None,
            new PropertyChangedCallback(OnMinValueChanged),
            new CoerceValueCallback(CoerceMinValue)
        ),
        new ValidateValueCallback(IsValidValue));

        //属性包装器,通过它来暴露Min的值
        public double MinValue
        {
            get { return (double)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }

        //注册Max依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
        public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(
            "MaxValue",
            typeof(double),
            typeof(MyValiDP),
            new FrameworkPropertyMetadata(
                double.NaN,
                FrameworkPropertyMetadataOptions.None,
                new PropertyChangedCallback(OnMaxValueChanged),
                new CoerceValueCallback(CoerceMaxValue)
            ),
            new ValidateValueCallback(IsValidValue)
        );

        //属性包装器,通过它来暴露Max的值
        public double MaxValue
        {
            get { return (double)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }

        //在CoerceCurrent加入强制判断赋值
        private static object CoerceCurrentValue(DependencyObject d, object value)
        {
            MyValiDP g = (MyValiDP)d;
            double current = (double)value;
            if (current < g.MinValue) current = g.MinValue;
            if (current > g.MaxValue) current = g.MaxValue;
            return current;
        }


        //当Current值改变的时候,调用Min和Max的CoerceValue回调委托
        private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MinValueProperty);
            d.CoerceValue(MaxValueProperty);
        }

        //当OnMin值改变的时候,调用Current和Max的CoerceValue回调委托
        private static void OnMinValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MaxValueProperty);
            d.CoerceValue(CurrentValueProperty);
        }

        //在CoerceMin加入强制判断赋值
        private static object CoerceMinValue(DependencyObject d, object value)
        {
            MyValiDP g = (MyValiDP)d;
            double min = (double)value;
            if (min > g.MaxValue) min = g.MaxValue;
            return min;
        }

        //在CoerceMax加入强制判断赋值
        private static object CoerceMaxValue(DependencyObject d, object value)
        {
            MyValiDP g = (MyValiDP)d;
            double max = (double)value;
            if (max < g.MinValue) max = g.MinValue;
            return max;
        }
        //当Max值改变的时候,调用Min和Current的CoerceValue回调委托
        private static void OnMaxValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MinValueProperty);
            d.CoerceValue(CurrentValueProperty);
        }

        //验证value是否有效,如果返回True表示验证通过,否则会提示异常
        public static bool IsValidValue(object value)
        {
            Double v = (Double)value;
            return (!v.Equals(Double.NegativeInfinity) && !v.Equals(Double.PositiveInfinity));
        }
    }
}


效果如下:


      上面的例子中,一共有三个依赖属性相互作用——CurrentValue、MinValue和MaxValue,这些属性相互作用,但它们的规则是MinValue≤CurrentValue≤MaxValue。根据这个规则,当其中一个依赖属性变化时,另外两个依赖 属性必须进行适当的调整,这里我们要用到的就是CoerceValue这个回调委托,那么实现起来也非常的简单,注册MaxValue的时候加入 CoerceValueCallback,在CoerceMaxValue函数中做处理:如果Maximum的值小于MinValue,则使 MaxValue值等于MinValue;同理在CurrentValue中也加入了CoerceValueCallback进行相应的强制 处理。然后在MinValue的ChangedValueCallback被调用的时候,调用CurrentValue和MaxValue的 CoerceValue回调委托,这样就可以达到相互作用的依赖属性一变应万变的”千机变“。

       换句话说,当相互作用的几个依赖属性其中一个发生变化时,在它的PropertyChangeCallback中调用受它影响的依赖属性的CoerceValue,这样才能保证相互作用关系的正确性。 前面也提高ValidateValue主要是验证该数据的有效性,最设置了值以后都会调用它来进行验证,如果验证不成功,则抛出异常。

12、依赖属性(三)
14、WPF中的数据绑定(一)

友情链接: CSDN激萌の小宅 95知识库 自考题库 罗分明个人网络博客 精益编程leanboot

小宅博客  www.bilibili996.com All Rights Reserved. 备案号: 闽ICP备2024034575号

网站经营许可证  福建省福州市 Copyright©2021-2025 版权所有

小宅博客
首页 智能家居 地图定位
公告:小宅博客网可以开发票了,需要发票的,去群里找群主哈!!

文章作者:激萌の小宅

促销:¥0

价格:¥0

配送方式: 购买后立即生效(如购买异常,请联系站长)
付款之后一定要等待自动跳转结束,否则购买可能会失败
  • 0 天

    有效期

  • 0

    总销量

  • 0

    累计评价

依赖属性(四) - (十三)


六、依赖属性回调、验证及强制值

       我们通过下面的这幅图,简单介绍一下WPF属性系统对依赖属性操作的基本步骤:


       借用一个常见的图例,介绍一下WPF属性系统对依赖属性操作的基本步骤: 

       第一步,确定Base Value,对同一个属性的赋值可能发生在很多地方。比如控件的背景(Background),可能在Style或者控件的构造函数中都对它进行了赋值,这个Base Value就要确定这些值中优先级最高的值,把它作为Base Value。

       第二步,估值。如果依赖属性值是计算表达式(Expression),比如说一个绑定,WPF属性系统就会计算表达式,把结果转化成一个实际值。

       第三步,动画。动画是一种优先级很高的特殊行为。如果当前属性正在作动画,那么因动画而产生的值会优于前面获得的值,这个也就是WPF中常说的动画优先。

       第四步,强制。如果我们在FrameworkPropertyMetadata中传入了 CoerceValueCallback委托,WPF属性系统会回调我们传入的的delagate,进行属性值的验证,验证属性值是否在我们允许的范围之内。例如强制设置该值必须大于于0小于10等等。在属性赋值过程中,Coerce拥有 最高的优先级,这个优先级要大于动画的优先级别。

       第五步,验证。验证是指我们注册依赖属性如果提供了ValidateValueCallback委托,那么最后WPF会调用我们传入的delegate,来验证数据的有效性。当数据无效时会抛出异常来通知。

       那么应该如何使用这些功能呢? 

       前面我们讲了基本的流程,下面我们就用一个小的例子来进行说明:

XAML的代码如下:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="400">
    <Grid>
        <StackPanel>
            <Button Name="btnDPTest" Click="btnDPTest_Click" >属性值执行顺序测试</Button>
        </StackPanel>
    </Grid>
</Window>


新增类SimpleDP.cs,如下:


MainWindow.xaml.cs代码如下:

using System.Windows;
using WpfApp.Models;

namespace WpfApp
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void btnDPTest_Click(object sender, RoutedEventArgs e)
        {
            SimpleDP test = new SimpleDP();
            test.ValidDP = 1;
        }
    }
}


SimpleDP.cs代码如下:

using System;
using System.Windows;

namespace WpfApp.Models
{
    public class SimpleDP : DependencyObject
    {
        public static readonly DependencyProperty ValidDPProperty =
            DependencyProperty.Register("ValidDP", typeof(int), typeof(SimpleDP),
                new FrameworkPropertyMetadata(0,
                    FrameworkPropertyMetadataOptions.None,
                    new PropertyChangedCallback(OnValueChanged),
                    new CoerceValueCallback(CoerceValue)),
                    new ValidateValueCallback(IsValidValue));

        public int ValidDP
        {
            get { return (int)GetValue(ValidDPProperty); }
            set { SetValue(ValidDPProperty, value); }
        }

        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Console.WriteLine("当属性值的OnValueChanged方法被调用,属性值为: {0}", e.NewValue);
        }

        private static object CoerceValue(DependencyObject d, object value)
        {
            Console.WriteLine("当属性值的CoerceValue方法被调用,属性值强制为: {0}", value);
            return value;
        }

        private static bool IsValidValue(object value)
        {
            Console.WriteLine("当属性值的IsValidValue方法被调用,对属性值进行验证,返回bool值,如果返回True表示严重通过,否则会以异常的形式抛出: {0}", value);
            return true;
        }
    }
}


执行结果如下:


【勘误】

       在博客园原文中对执行结果的描述不对,按站长的测试结果来看,当ValidDP属性变化之后,确实按照流程,先 Coerce后Validate的顺序执行了。


【原文如下】

       ValidDP属性变化之后,PropertyChangeCallback就会被调用。可以看到结果并没有完全按照我们先前的流程先 Coerce后Validate的顺序执行,有可能是WPF内部做了什么特殊处理,当属性被修改时,首先会调用Validate来判断传入的value是 否有效,如果无效就不继续后续的操作,这样可以更好的优化性能。从上面的结果上看出,CoerceValue后面并没有立即ValidateValue, 而是直接调用了PropertyChanged。这是因为前面已经验证过了value,如果在Coerce中没有改变value,那么就不用再验证了。如 果在 Coerce中改变了value,那么这里还会再次调用ValidateValue操作,和前面的流程图执行的顺序一样,在最后我们会调用 ValidateValue来进行最后的验证,这就保证最后的结果是我们希望的那样了。

       上面简单介绍了处理流程,下面我们就以一个案例来具体看一看上面的流程到底有没有出入。


XAML代码如下:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp.Controls"
        Title="MainWindow" Height="400" Width="500">
    <Grid>
        <StackPanel Orientation="Vertical">
            <local:MyValiDP x:Name="myValiDP1" MaxValue="500" MinValue="0" />
            <Label Content="可以设置最小值为0和最小大值为500" Height="30"/>
            <StackPanel Orientation="Horizontal" Height="60">
                <Label Content="当前值为 : "/>
                <Label Background="Yellow" BorderBrush="Black" BorderThickness="1"
                   IsEnabled="False" Content="{Binding ElementName=myValiDP1, Path=CurrentValue}" Height="25" VerticalAlignment="Top" />
            </StackPanel>

            <WrapPanel >
                <Label Content="最小值" />
                <Slider x:Name="sliderMin" Minimum="-200" Maximum="100" Width="300" ValueChanged="sliderMin_ValueChanged" SmallChange="10"  />
                <Label Content="{Binding ElementName=sliderMin, Path=Value}" />
            </WrapPanel>
            <WrapPanel >
                <Label Content="最大值" />
                <Slider x:Name="sliderMax" Minimum="200" Maximum="800" Width="300" ValueChanged="sliderMax_ValueChanged" SmallChange="10" />
                <Label Content="{Binding ElementName=sliderMax, Path=Value}" />
            </WrapPanel>
        </StackPanel>
    </Grid>
</Window>


MainWindow.xaml.cs代码如下:

using System.Windows;

namespace WpfApp
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //设置Current的值
            myValiDP1.CurrentValue = 100;
        }
        private void sliderMin_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            //设置Current的值
            myValiDP1.CurrentValue = (int)sliderMin.Value;
        }
        private void sliderMax_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {

            //设置Current的值
            myValiDP1.CurrentValue = (int)sliderMax.Value;

        }
    }
}


依赖属性代码MyValiDP.cs文件如下:

using System;
using System.Windows;

namespace WpfApp.Controls
{
    class MyValiDP : System.Windows.Controls.Control
    {
        //注册Current依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
        public static readonly DependencyProperty CurrentValueProperty = DependencyProperty.Register(
            "CurrentValue",
            typeof(double),
            typeof(MyValiDP),
            new FrameworkPropertyMetadata(
                Double.NaN,
                FrameworkPropertyMetadataOptions.None,
                new PropertyChangedCallback(OnCurrentValueChanged),
                new CoerceValueCallback(CoerceCurrentValue)
            ),
            new ValidateValueCallback(IsValidValue)
        );

        //属性包装器,通过它来暴露Current的值
        public double CurrentValue
        {
            get { return (double)GetValue(CurrentValueProperty); }
            set { SetValue(CurrentValueProperty, value); }
        }

        //注册Min依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
        public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(
        "MinValue",
        typeof(double),
        typeof(MyValiDP),
        new FrameworkPropertyMetadata(
            double.NaN,
            FrameworkPropertyMetadataOptions.None,
            new PropertyChangedCallback(OnMinValueChanged),
            new CoerceValueCallback(CoerceMinValue)
        ),
        new ValidateValueCallback(IsValidValue));

        //属性包装器,通过它来暴露Min的值
        public double MinValue
        {
            get { return (double)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }

        //注册Max依赖属性,并添加PropertyChanged、CoerceValue、ValidateValue的回调委托
        public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(
            "MaxValue",
            typeof(double),
            typeof(MyValiDP),
            new FrameworkPropertyMetadata(
                double.NaN,
                FrameworkPropertyMetadataOptions.None,
                new PropertyChangedCallback(OnMaxValueChanged),
                new CoerceValueCallback(CoerceMaxValue)
            ),
            new ValidateValueCallback(IsValidValue)
        );

        //属性包装器,通过它来暴露Max的值
        public double MaxValue
        {
            get { return (double)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }

        //在CoerceCurrent加入强制判断赋值
        private static object CoerceCurrentValue(DependencyObject d, object value)
        {
            MyValiDP g = (MyValiDP)d;
            double current = (double)value;
            if (current < g.MinValue) current = g.MinValue;
            if (current > g.MaxValue) current = g.MaxValue;
            return current;
        }


        //当Current值改变的时候,调用Min和Max的CoerceValue回调委托
        private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MinValueProperty);
            d.CoerceValue(MaxValueProperty);
        }

        //当OnMin值改变的时候,调用Current和Max的CoerceValue回调委托
        private static void OnMinValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MaxValueProperty);
            d.CoerceValue(CurrentValueProperty);
        }

        //在CoerceMin加入强制判断赋值
        private static object CoerceMinValue(DependencyObject d, object value)
        {
            MyValiDP g = (MyValiDP)d;
            double min = (double)value;
            if (min > g.MaxValue) min = g.MaxValue;
            return min;
        }

        //在CoerceMax加入强制判断赋值
        private static object CoerceMaxValue(DependencyObject d, object value)
        {
            MyValiDP g = (MyValiDP)d;
            double max = (double)value;
            if (max < g.MinValue) max = g.MinValue;
            return max;
        }
        //当Max值改变的时候,调用Min和Current的CoerceValue回调委托
        private static void OnMaxValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(MinValueProperty);
            d.CoerceValue(CurrentValueProperty);
        }

        //验证value是否有效,如果返回True表示验证通过,否则会提示异常
        public static bool IsValidValue(object value)
        {
            Double v = (Double)value;
            return (!v.Equals(Double.NegativeInfinity) && !v.Equals(Double.PositiveInfinity));
        }
    }
}


效果如下:


      上面的例子中,一共有三个依赖属性相互作用——CurrentValue、MinValue和MaxValue,这些属性相互作用,但它们的规则是MinValue≤CurrentValue≤MaxValue。根据这个规则,当其中一个依赖属性变化时,另外两个依赖 属性必须进行适当的调整,这里我们要用到的就是CoerceValue这个回调委托,那么实现起来也非常的简单,注册MaxValue的时候加入 CoerceValueCallback,在CoerceMaxValue函数中做处理:如果Maximum的值小于MinValue,则使 MaxValue值等于MinValue;同理在CurrentValue中也加入了CoerceValueCallback进行相应的强制 处理。然后在MinValue的ChangedValueCallback被调用的时候,调用CurrentValue和MaxValue的 CoerceValue回调委托,这样就可以达到相互作用的依赖属性一变应万变的”千机变“。

       换句话说,当相互作用的几个依赖属性其中一个发生变化时,在它的PropertyChangeCallback中调用受它影响的依赖属性的CoerceValue,这样才能保证相互作用关系的正确性。 前面也提高ValidateValue主要是验证该数据的有效性,最设置了值以后都会调用它来进行验证,如果验证不成功,则抛出异常。