【WPF学习】第十二章 属性验证

  在定义任何类型的属性时,都需要面对错误设置属性的可能性。对于传统的.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

时间: 2024-10-09 16:33:51

【WPF学习】第十二章 属性验证的相关文章

第十二章.属性

来源 属性分为计算属性.存储属性.类型属性 另外,还可以定义属性监视器来监控属性值的变化.属性监视器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上. 存储属性 简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量. 下面的例子定义了一个名为FixedLengthRange的结构体,他描述了一个在创建后无法修改值域宽度的区间: struct FixedLengthRange { var firstValue: Int let length: Int } var ra

python学习第二十二章

今日内容: 1.属性 2. 类方法 3.静态方法 4.反射 1. 属性 是指调用类中的函数,就像调用对象属性值一样调用.更权威解释:将方法 伪装 成属性,虽然在代码层面上没有任何高深之处,但是让其看起来更合理. 示例: class A: def __init__(self,*args): self.name = args[0] self.height = args[1] self.weight = args[2] @property def bmi(self): return self.heig

【WPF学习】第三十二章 执行命令

原文:[WPF学习]第三十二章 执行命令 前面章节已经对命令进行了深入分析,分析了基类和接口以及WPF提供的命令库.但尚未例举任何使用这些命令的例子. 如前所述,RoutedUICommand类没有任何硬编码的功能,而是只表达命令,为触发命令,需要有命令源(也可使用代码).为响应命令,需要有命令绑定,命令绑定将执行转发给普遍的事件处理程序. 一.命令源 命令库中的命令始终可用.触发他们的最简单的方法是将它们关联到实现了ICommandSource接口的控件,其中包括继承自ButtonBase类的

WPF学习系列之二 (依赖项属性)

依赖属性;(dependency property)  它是专门针对WPF创建的,但是WPF库中的依赖项属性都使用普通的.NET属性过程进行了包装.从而可能通过常规的方式使用它们,即使使用他们的代码不理解WPF依赖项属性系统也是如此,使用旧技术包装新技术看起来有些奇怪,但这正是WPF能够改变基础组成部分,而不会扰乱.NET领域中其他部分的原因.三步:一:定义依赖项属性.public static readonly DependencyProperty MarginProperty;二:.在静态构

【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程

原文:[.NET Core项目实战-统一认证平台]第十二章 授权篇-深入理解JWT生成及验证流程 [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章介绍了基于Ids4密码授权模式,从使用场景.原理分析.自定义帐户体系集成完整的介绍了密码授权模式的内容,并最后给出了三个思考问题,本篇就针对第一个思考问题详细的讲解下Ids4是如何生成access_token的,如何验证access_token的有效性,最后我们使用.net webapi来实现一个外部接口(本来想用JAVA来实现的,

《构建之法》第十一、十二章学习总结

第十一章的内容是软件设计与实现. 在第一节中,讲的是关于分析和设计方法,向我们介绍在"需求分析"."设计与实现"阶段."测试""发布"阶段该搞清楚的问题. 在第二节中,讲的是关于图形建模和分析方法.在表达实体和实体之间的关系时,可以用到思维导图(Mind Map).实体关系图(ERD).UCD ;在表达数据的流动时,可以用到DFD工具:在表达控制流的时候可以用到FSM工具:前面提到的这些图形建模方法各有特点,UML却可以有一个

JavaScript DOM编程艺术-学习笔记(第十二章)

第十二章 1.本章是综合前面章节的所有东西的,一个综合实例 2.流程:①项目简介:a.获取原始资料(包括文本.图片.音视频等) b.站点结构(文件目录结构) c.页面(文件)结构 ②设计(切图) ③css -  base.css用于引入使用的css文件 color.css  - 用于设置样式 layout.css - 用于设置布局 Typography.css - 用于设置版式 3.题外话:①在实际开发中,即使是一个空白项目也往往不会从一无所有做起,而借助的平台一般会提供目录结构,所以需要把自己

Java学习笔记—第十二章 Java网络编程入门

第十二章  Java网络编程入门 Java提供的三大类网络功能: (1)URL和URLConnection:三大类中最高级的一种,通过URL网络资源表达方式,可以很容易确定网络上数据的位置.利用URL的表示和建立,Java程序可以直接读入网络上所放的数据,或把自己的数据传送到网络的另一端. (2)Socket:又称"套接字",用于描述IP地址和端口(在Internet中,网络中的每台主机都有一个唯一的IP地址,而每台主机又通过提供多个不同端口来提供多种服务).在客户/服务器网络中,当客

第十二章 并发编程 学习笔记

第十二章 并发编程 进程是程序级并发,线程是函数级并发. 三种基本的构造并发程序的方法: 进程:每个逻辑控制流是个一个进程,由内核进行调度和维护. I/O多路复用:应用程序在一个进程的上下文中显式地调度他们自己的逻辑流. 线程:运行在单一进程上下文中的逻辑流,由内核进行调度. 12.1 基于进程的并发编程 构造并发程序最简单的方法就是用进程. 使用大家都很熟悉的函数例如: fork exec waitpid 关于在父.子进程间共享状态信息:共享文件表,但不共享用户地址空间. 进程又独立的地址空间