我们通过下面的这幅图,简单介绍一下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="WpfApp1.WindowValid"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title=" WindowValid " Height="300" Width="400"><Grid><StackPanel>     <Button Name="btnDPTest" Click="btnDPTest_Click" >属性值执行顺序测试</Button></StackPanel></Grid></Window>

<

C#的代码如下:


using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Imaging;using System.Windows.Shapes;using System.Windows.Threading;using WpfApp1.Models;namespace WpfApp1{/// <summary>/// WindowThd.xaml 的交互逻辑/// </summary>public partial class WindowValid: Window{public WindowValid (){InitializeComponent();}private void btnDPTest_Click(object sender, RoutedEventArgs e){SimpleDP test = new SimpleDP();test.ValidDP = 1;} }}``

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

namespace WpfApp1.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属性变化之后,PropertyChangeCallback就会被调用。可以看到结果并没有完全按照我们先前的流程先 Coerce后Validate的顺序执行,有可能是WPF内部做了什么特殊处理,当属性被修改时,首先会调用Validate来判断传入的value是 否有效,如果无效就不继续后续的操作,这样可以更好的优化性能。从上面的结果上看出,CoerceValue后面并没有立即ValidateValue, 而是直接调用了PropertyChanged。这是因为前面已经验证过了value,如果在Coerce中没有改变value,那么就不用再验证了。如 果在 Coerce中改变了value,那么这里还会再次调用ValidateValue操作,和前面的流程图执行的顺序一样,在最后我们会调用 ValidateValue来进行最后的验证,这就保证最后的结果是我们希望的那样了。上面简单介绍了处理流程,下面我们就以一个案例来具体看一看上面的流程到底有没有出入。依赖属性代码文件如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

namespace WpfApp1.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));}}

}

“`

XAML代码如下:

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApp1.Controls"Title="WindowProcess" 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>

C#代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Shapes;

namespace WpfApp1
{

/// <summary>
/// WindowProcess.xaml 的交互逻辑
/// </summary>public partial class WindowProcess : Window
{public WindowProcess(){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;}}

}

在上面的例子中,一共有三个依赖属性相互作用——CurrentValue、MinValue和MaxValue,这些属性相互作 用,但它们的规则是MinValue≤CurrentValue≤MaxValue。根据这个规则,当其中一个依赖属性变化时,另外两个依赖 属性必须进行适当的调整,这里我们要用到的就是CoerceValue这个回调委托,那么实现起来也非常的简单,注册MaxValue的时候加入 CoerceValueCallback,在CoerceMaxValue函数中做处理:如果Maximum的值小于MinValue,则使 MaxValue值等于MinValue;同理在CurrentValue中也加入了CoerceValueCallback进行相应的强制 处理。然后在MinValue的ChangedValueCallback被调用的时候,调用CurrentValue和MaxValue的 CoerceValue回调委托,这样就可以达到相互作用的依赖属性一变应万变的”千机变“。
换句话说,当相互作用的几个依赖属性其中一个发生变化时,在它的PropertyChangeCallback中调用受它影响的依赖属性的CoerceValue,这样才能保证相互作用关系的正确性。 前面也提高ValidateValue主要是验证该数据的有效性,最设置了值以后都会调用它来进行验证,如果验证不成功,则抛出异常。

WPF入门教程(八)--依赖属性(4)相关推荐

  1. WPF入门教程(八)--依赖属性(4)(转)

    WPF入门教程(八)--依赖属性(4) 2018年08月27日 11:35:55 weixin_38029882 阅读数:71 我们通过下面的这幅图,简单介绍一下WPF属性系统对依赖属性操作的基本步骤 ...

  2. WPF入门教程(七)---依赖属性(3)(转)

    WPF入门教程(七)---依赖属性(3) 2018年08月24日 08:33:43 weixin_38029882 阅读数:50 四. 只读依赖属性 在以前在对于非WPF的功能来说,对于类的属性的封装 ...

  3. WPF入门教程系列十三——依赖属性(三)

    四. 只读依赖属性 在以前在对于非WPF的功能来说,对于类的属性的封装中,经常会对那些希望暴露给外界只读操作的字段封装成只读属性,同样在WPF中也提供了只读属性的概念,如一些 WPF控件的依赖属性是只 ...

  4. WPF入门教程系列四——Dispatcher介绍

    WPF入门教程系列四--Dispatcher介绍 一.Dispatcher介绍 微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢? 不管是WinForm应用程序还 ...

  5. WPF入门教程系列三——Application介绍(续)

    接上文WPF入门教程系列二--Application介绍,我们继续来学习Application 三.WPF应用程序的关闭 WPF应用程序的关闭只有在应用程序的 Shutdown 方法被调用时,应用程序 ...

  6. WPF 入门教程(一)

    WPF 入门教程(一) 1.布局规则 1.WPF 窗体中,一个窗体只能持有一个空间,当需要展示多个控件时,则需要首先设置一个容器控件(Container).控件的布局有容器来决定. 2.控件应避免明确 ...

  7. WPF入门教程-转载

    最近为了做炫酷的UI,了解了WPF,之前一直是使用winform的,界面也是古老的不行. 在园里找到了一个大佬以前写的教程,备注一下.按照系列教程走下来,可以直接上手了. 备忘传送门>>& ...

  8. WPF xaml中列表依赖属性的定义

    原文:WPF xaml中列表依赖属性的定义 列表内容属性 如上图,是一个列表标题排序控件,我们需要定义一个标题列表,从而让调用方可以自由的设置标题信息. 在自定义控件时,会遇到列表依赖属性,那么该如何 ...

  9. WPF学习笔记一 依赖属性及其数据绑定

    本文想通过由浅入深的讲解让读者比较深的理解依赖属性.  首先,我们回顾一下依赖属性的发展历史. 最初,人们提出面向对象编程时,并没有属性这个说法,当时叫做成员变量.一个对象由成员变量和成员函数组成,如 ...

  10. WPF入门教程教学(转载)

    WPF入门教程(1)-基础 https://blog.csdn.net/weixin_38029882/article/details/81867294 WPF入门教程(2)-基础篇 https:// ...

最新文章

  1. java实现qq_java实现的类似qq聊天系统
  2. 6.1 Tensorflow笔记(基础篇):队列与线程
  3. 【震惊】史上最牛的市场推广/营销
  4. 关于解决oracle登录:ora-12154:tns:无法解析指定的连接标识符
  5. 【算法学习笔记】07.数据结构基础 链表 初步练习
  6. 检索COM类工厂中CLSID为{000209FF-0000-0000-C000-000000000046}的组件时失败,原因是出现以下错误: 80070005...
  7. UVA 11549 Calculator Conundrum
  8. 新iPhone全贴合保护壳曝光:“浴霸”造型恐已成定局
  9. 如何使用Windows、Ubuntu甚至手机登录远程计算机
  10. 数组使用方法集合(建议收藏)
  11. Swift中的willSet与didSet
  12. 计算机科学导论第8章答案,第8章计算机科学导论.ppt
  13. 数据结构课程设计(已完结)
  14. 这8款Android桌面插件,这款 Android 应用,帮你优雅地管理桌面小部件
  15. linux系统日志及其管理
  16. DockerHub上最受欢迎的151个官方镜像,相传掌握第17个可以主宰宇宙!
  17. Linux的基本知识和基础操作
  18. maya2020卸载不干净_MAYA 卸载不干净,怎么完全彻底删除清理干净MAYA各种残留注册表和文件?...
  19. 软件测试基础知识 - 说一说黑盒与白盒的测试方法
  20. 使用计算机开机按啥建,学生计算器第一次使用应该如何开机

热门文章

  1. swift 3 和 xcode 8
  2. 局域网ARP攻击问题
  3. JVM学习(七):运行时数据区(精讲)
  4. View绘制体系(三)——AttributeSet与TypedArray详解
  5. 阳光点吧,颓废已经过时了
  6. Android 游戏闯关
  7. 如何看待铺天盖地都是Python的广告?
  8. CHS和LBA逻辑块地址
  9. 计算机探索策略的反馈调整,任务驱动模式下的中职计算机应用基础课程优化教学探索...
  10. 如何快速创建k8syaml文件模板