WPF中数据绑定验证深入讲解

WPF中数据绑定验证深入讲解

WPF在用户输入时,提供了验证功能,通常验证使用以下两种方式来实现:

  1. 在数据对象中引发错误。通常是在属性设置过程中抛出异常,或者在数据类中实现INotifyDataErrorInfoIDataErrorInfo接口。
  2. 在绑定级别定义验证。

只有来自目标的值正在被用于更新数据源时才会应用验证。

数据对象中设置验证

  1. 在属性中Set上抛出异常
public class MyData
{private string _value = "200";public string Value{get { return _value; }set{_value = value;if (value == "123")throw new System.Exception("报错了~~~[Exception]");}}
}
  1. 直接抛出异常,wpf经常会忽略,从而得不到异常的信息,此时需要借助ExceptionValidationRule

ExceptionValidationRule是预先构建的验证规则,它向WPF发出所有的异常报告。它必须在<Binding.ValidationRules>里面

<TextBox x:Name="tb1"><TextBox.Text><Binding Path="Value" UpdateSourceTrigger="PropertyChanged"><Binding.ValidationRules><ExceptionValidationRule /></Binding.ValidationRules></Binding></TextBox.Text>
</TextBox>

ExceptionValidationRule在绑定过程中发生的所有异常,包括编辑的值不能转为正确类型、属性设置器异常以及值转换器异常(float转为string)。当出现验证失败后,System.Windows.Controls.Validation类的附加属性会记录下错误:

  • 在绑定元素上,Validation.HasError为True,同时会自动将控件的模板切换为Validation.ErrorTemplate定义的模板。
  • ValidationRule.Validate()会返回ValidationError,其中中包含错误细节
  • 如果Binding.NotifyOnValidationError被设置为True,则会在绑定元素上引发Validation.Error事件

INotifyDataErrorInfo

INotifyDataErrorInfoINotifyDataErrorInfo都有类似作用,但是INotifyDataErrorInfo界面更加丰富。与上面不同的是,实现INotifyDataErrorInfoIDataErrorInfo接口时,允许用户修改为非法值,只不过给出错误提示。

image-20231108111152474

使用INotifyDataErrorInfo的案例

  1. 新建一个Data类
//类实现了INotifyDataErrorInfo接口,该接口定义了HasErrors属性和GetErrors方法,以及ErrorsChanged事件
public class Data : INotifyDataErrorInfo,INotifyPropertyChanged
{//key为属性名,value为错误信息列表Dictionary<string, List<string>> errors = new();void SetErrors(string propertyName, List<string> value){errors.Remove(propertyName);errors.Add(propertyName, value);if (ErrorsChanged != null){ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));}}void ClearErrors(string propertyName){errors.Remove(propertyName);if (ErrorsChanged != null){ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));}}public bool HasErrors => errors.Count>0;public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;public event PropertyChangedEventHandler? PropertyChanged;public IEnumerable Errors => GetErrors("ModelNumber");public IEnumerable GetErrors(string? propertyName){if (propertyName is null or { Length: <= 0 }){return errors.Values;}else{if (errors.ContainsKey(propertyName)){return errors[propertyName];}else{return null;}}}private string modelNumber;public string ModelNumber{get { return modelNumber; }set { modelNumber = value;bool valid = true;foreach (char c in modelNumber){if (!char.IsLetterOrDigit(c)){valid = false;  break;}}if (!valid){List<string> errors = new(); errors.Add("ModelNumber不能含有标点符号,空格等");SetErrors("ModelNumber", errors);}else{ClearErrors("ModelNumber");}PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ModelNumber"));PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("HasErrors"));PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Errors"));}}
}
  1. 做一个界面,绑定ModelNumber
<Window ...><Window.DataContext><local:Data/></Window.DataContext><StackPanel><TextBox Text="{Binding ModelNumber ,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"/><TextBlock><Run Text="是否有错误"/><Run Text="{Binding HasErrors, Mode=OneWay}"/></TextBlock><ListView ItemsSource="{Binding Errors}"/></StackPanel>
</Window>

动图

自定义验证规则

自定义验证规则很像自定义转换器

  1. 针对某个属性自定义验证规则
public class ValueRule : ValidationRule
{public override ValidationResult Validate(object value, CultureInfo cultureInfo){if (value?.ToString() == "123") return new ValidationResult(false, "输入的值不在范围内");return new ValidationResult(true, null);}
}
  1. 界面上使用验证规则
<StackPanel><TextBox><TextBox.Text><Binding Path="Max" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay"><Binding.ValidationRules><local:ValueRule/></Binding.ValidationRules></Binding></TextBox.Text></TextBox>
</StackPanel>

可以看出,<Binding.ValidationRules>下面可以放置多个验证规则,按顺序执行,当所有的验证规则都通过后,则调用转换器(如果存在),其中ExceptionValidationRule比较特殊,当输入内容不能转换为其他规则所定义的转换时,也会触发。

错误显示

首先只有设置了Binding.NotifyOnValidationError为true时,才会引发Validation.Error事件,当含有错误时,可以使用静态类Validation中的附加属性ErrorsHasError来获取信息。

通常出现错误时,边框显示未红色,也可以自行设置错误模板,错误模板位于装饰层,它位于普通窗口内容之上。

<TextBox Width="130"><TextBox.Text><BindingMode="TwoWay"Path="Max"UpdateSourceTrigger="PropertyChanged"><Binding.ValidationRules><local:ValueRule /></Binding.ValidationRules></Binding></TextBox.Text><Validation.ErrorTemplate><ControlTemplate><DockPanel LastChildFill="True"><TextBlockDockPanel.Dock="Right"Foreground="Red"Text="*" /><Border BorderBrush="Green" BorderThickness="2"><AdornedElementPlaceholder /></Border></DockPanel></ControlTemplate></Validation.ErrorTemplate>
</TextBox>

image-20231108140348940

其中AdornedElementPlaceholder代表控件本身,上面案例中是将*放入了控件周围,如果想将*重叠放到控件上面,可以使用Grid,放在同一窗格。

<Validation.ErrorTemplate><ControlTemplate><Grid><TextBlockMargin="50,5,0,0"DockPanel.Dock="Right"Foreground="Red"Text="*" /><Border BorderBrush="Green" BorderThickness="2"><AdornedElementPlaceholder /></Border></Grid></ControlTemplate>
</Validation.ErrorTemplate>

image-20231108141002670

但是这样显示不出错误信息,可以使用ToolTip来显示第一个错误内容

<Validation.ErrorTemplate><ControlTemplate><Grid><TextBlockMargin="50,5,0,0"DockPanel.Dock="Right"Foreground="Red"Text="*"ToolTip="{Binding ElementName=adornerPlaceholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" /><Border BorderBrush="Green" BorderThickness="2"><AdornedElementPlaceholder x:Name="adornerPlaceholder" /></Border></Grid></ControlTemplate>
</Validation.ErrorTemplate>

上面模板中使用了AdornedElementPlaceholderAdornedElement属性指向背后的元素。

动图

这样只有悬浮在后面的*号时才会显示错误信息,如果想作为TextBox元素本身的ToolTip,可借助Validation.HasError可以实现。

<TextBox Width="130"><TextBox.Text><BindingMode="TwoWay"Path="Max"UpdateSourceTrigger="PropertyChanged"><Binding.ValidationRules><local:ValueRule /></Binding.ValidationRules></Binding></TextBox.Text><Validation.ErrorTemplate><ControlTemplate><Grid><TextBlockMargin="50,5,0,0"DockPanel.Dock="Right"Foreground="Red"Text="*"ToolTip="{Binding ElementName=adornerPlaceholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" /><Border BorderBrush="Green" BorderThickness="2"><AdornedElementPlaceholder x:Name="adornerPlaceholder" /></Border></Grid></ControlTemplate></Validation.ErrorTemplate><TextBox.Style><Style TargetType="TextBox"><Style.Triggers><Trigger Property="Validation.HasError" Value="True"><Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=(Validation.Errors)[0].ErrorContent}" /></Trigger></Style.Triggers></Style></TextBox.Style>
</TextBox>

动图

验证多个值

很多时候需要动态验证多个绑定值,比如有两个属性,一个Max,一个Min,要求是用户输入Min必须小于Max,要实现这个功能可以使用绑定组来创建。

绑定组的原理很简单,同样是创建继承自ValidationRule的类,不同的是,不能将该规则绑定到单个绑定表达式,而是将其附加到包含所有绑定控件的容器上。

  1. ViewModel中有两个属性
public class Data : INotifyDataErrorInfo,INotifyPropertyChanged
{public int Max { set; get; } = 100;public int Min { set; get; } = 1;
}
  1. 创建验证规则
public class ValueRule : ValidationRule
{public override ValidationResult Validate(object value, CultureInfo cultureInfo){BindingGroup bindingGroup = (BindingGroup)value;var d= (Data)bindingGroup.Items[0];if (d.Min >= d.Max){return new ValidationResult(false, "错误,最小值必须小于最大值");}return new ValidationResult(true, null);}
}
  1. UI上绑定,注意,此处要在Grid中添加绑定组
<Grid Margin="60" TextBox.LostFocus="Grid_LostFocus"><Grid.BindingGroup><BindingGroup x:Name="customGroup"><BindingGroup.ValidationRules><local:ValueRule /></BindingGroup.ValidationRules></BindingGroup></Grid.BindingGroup><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><TextBoxx:Name="ddd"Grid.Row="0"Text="{Binding Path=Max, BindingGroupName=customGroup, UpdateSourceTrigger=PropertyChanged}" /><TextBox Grid.Row="1" Text="{Binding Path=Min, BindingGroupName=customGroup, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
  1. 此时并不会验证,绑定组使用了事务处理编辑系统,只有正式提交后才会进行验证,所以在Grid上增加事件,当TextBox失去焦点时触发
private void Grid_LostFocus(object sender, RoutedEventArgs e)
{customGroup.CommitEdit();
}
  1. 如果验证失败,则整个Grid会认为是无效的。

    动图

注意:

  1. 当存在多个绑定组时,要为BindingGroup设置Name,这样可以在具体绑定时设置绑定组Text="{Binding Path=Max, BindingGroupName=customGroup, UpdateSourceTrigger=PropertyChanged}" />
  2. 默认情况时,Validate方法中接收到的数据是原始对象,而不是新修改的值,所以为了验证新值,可以使用GetValue方法
BindingGroup bindingGroup = (BindingGroup)value;
var d = (Data)bindingGroup.Items[0];
var newValue = bindingGroup.GetValue(d, "Min");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/184633.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

技术分享 | 测试平台开发-前端开发之数据展示与分析

测试平台的数据展示与分析&#xff0c;我们主要使用开源工具ECharts来进行数据的展示与分析。 ECharts简介与安装 ECharts是一款基于JavaScript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表&#xff…

Appium —— 初识移动APP自动化测试框架Appium

说到移动APP自动化测试&#xff0c;代表性的测试框架非Appium莫属&#xff0c;从今天开始我们将从APP结构解析、Appium框架学习、安卓/iOS自动化测试实战、自动遍历回归测试、自动化测试平台及持续集成&#xff0c;多个维度一起由浅入深的学废Appium 今天我们先来初步认识Appi…

C++中将数据添加到文件的末尾

参考:https://blog.csdn.net/qq_23880193/article/details/44279283 C中文件的读取需要包含fstream文件&#xff0c;即&#xff1a;#include 文件的读取和写入是是通过流操作来的&#xff0c;这不像输入、输出流那样&#xff0c;库中已经定义了对象cin和cout 文件的读取需要声…

加速度jsudo:小企业会遇到哪些瓶颈期?

什么是瓶颈期&#xff1f;瓶颈期&#xff0c;就是你无论怎么努力&#xff0c;成绩都是上不去&#xff0c;还是停留在原地&#xff1b;而自己表现的还是很匆忙&#xff0c;却不知道如何下手&#xff1f;就像水桶效益一样&#xff0c;水桶的木板高度层次不齐&#xff0c;像极了自…

Spark Core

Spark Core 本文来自 B站 黑马程序员 - Spark教程 &#xff1a;原地址 第一章 RDD详解 1.1 为什么需要RDD 分布式计算需要 分区控制shuffle控制数据存储、序列化、发送数据计算API等一系列功能 这些功能&#xff0c;不能简单的通过Python内置的本地集合对象&#xff08;如…

在外包干了3年,彻底废了...

前言 先简单说下&#xff0c;我18年的大专生&#xff0c;通过校招去了一家软件公司&#xff0c;在里面干了快3年的功能测试&#xff0c;后面我感觉自己不能够在这样下去了&#xff0c;长时间重复性工作且呆在一个舒适的环境会毁掉一个人&#xff0c;而我已经在一个企业干了3年…

技术分享 | Appium 用例录制

下载及安装 下载地址&#xff1a; github.com/appium/appi… 下载对应系统的 Appium 版本&#xff0c;安装完成之后&#xff0c;点击 “Start Server”&#xff0c;就启动了 Appium Server。 在启动成功页面点击右上角的放大镜&#xff0c;进入到创建 Session 页面。配置好…

PostCSS通过px2rem插件和lib-flexible将px单位转换为rem(root em)单位实现大屏适配

目录 文档postcss中使用postcss-plugin-px2rem安装postcss-plugin-px2rem示例默认配置 webpack中使用postcss-plugin-px2rem项目结构安装依赖文件内容 大屏适配参考文章 文档 类似的插件 postcss-plugin-px2rem https://www.npmjs.com/package/postcss-plugin-px2remhttps://g…

嵌入式系统设计与应用---ARM指令集(学习笔记)

目录 本文图片截取自书本和老师的ppt 概述 指令格式 指令的条件码 ARM的寻址方式 立即寻址 寄存器寻址 寄存器间接寻址 寄存器移位寻址 变址寻址 多寄存器寻址 相对寻址 堆栈寻址 块复制寻址 ARM指令集简介 跳转指令 1.B指令 2.BL指令 数据处理指令 1.数据传…

南昌大学漏洞报送证书

获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;https://src.sjtu.edu.cn/ 兑换价格&#xff1a;20金币 获取条件&#xff1a;南昌大学任意中危或以上级别漏洞

XShelll-修改快捷键-xftp-修改编辑器

文章目录 1.XShelll-修改快捷键2.Xftp-修改文本编辑器3.总结 1.XShelll-修改快捷键 工具>选项 鼠标键盘&#xff0c;右键编辑&#xff0c;新建快捷键。 复制粘贴改成shiftc,shiftv。更习惯一些。 2.Xftp-修改文本编辑器 xftp修改服务器文件默认的编辑器&#xff0c;是记…

我是如何快速入门音视频开发的?

最近有读者留言&#xff0c;说“想转行音视频开发&#xff0c;怎么做”&#xff0c;正巧&#xff0c;前几天我还在知乎上&#xff0c;看到有人在问音视频的学习资料&#xff0c;还是个大一的学生。 想说一句&#xff1a;真有眼光。 如今这个时代&#xff0c;想赚钱&#xff0c…

动手学Matplotlib画图,Matplotlib 是一个非常强大的 Python 画图工具。【Matplotlib学习笔记】

一、第一章 1.基本用法 import matplotlib.pyplot as plt import numpy as npx np.linspace(-1,1,50) y 2*x 1 plt.plot(x,y) plt.show()2.figure图像 import matplotlib.pyplot as plt import numpy as npx np.linspace(-1,1,50) y1 2*x 1 y2 x**2 plt.figure() plt…

Java http请求工具连接超时时间

研究了一下三种java常用的http请求工具框架hutool、okhttp3、spring RestTemplate 对于连接超时和读超时的处理机制。 运行环境 jdk8 windows 连接超时 hutool、okhttp3、spring RestTemplate 三种请求&#xff0c;底层使用的都是jdk里的java.net.DualStackPlainSocketImpl#…

Linux 安装 Nginx 并配置为系统服务(超详细)

目录 前言安装 Nginx安装依赖项下载Nginx解压Nginx编译和安装防火墙设置启动Nginx 配置 Nginx 为系统服务配置 Nginx 服务文件启动 Nginx 服务设置开机自启动检查 Nginx 状态停止 Nginx 服务重启 Nginx 服务 卸载 Nginx结语 前言 Nginx是一款卓越的高性能Web服务器&#xff0c…

概率论和数理统计(二) 数字特征与大数定律

前言 有了“概率”数据,怎么反应情况.数学期望与方差,大数,极限 数学期望 期望是数字特征之一,其描述的是随机试验在同样的机会下重复多次&#xff0c;所有那些可能状态的平均结果. 平均数和加权平均数 离散型随机变量期望 连续型随机变量期望 随机变量函数的期望 g ( x , …

Java EE进阶2

包如果下载不下来怎么办? 1,确认包是否存在 2.如果包存在就多下载几次 3.如果下载了很多次都下载不下来,看看是不是下面几步出现了问题? 1)是否配置了国内源 settings.xml 2)目录是否为全英文,存在中文的话就修改路径 3)删除本地仓库的 jar 包,重新下载(可能由于网络的原…

华为取消6000万订单影响在扩大,高通嘴硬强调不受影响

高通公布了2023年第三季度的业绩&#xff0c;业绩显示营收下滑24%&#xff0c;净利润下滑36%&#xff0c;不过高通强调预计今年四季度业绩将回升&#xff0c;意思是说华为取消订单带来的影响较小。 一、高通处境不利已延续4年时间 2019年美国对华为采取措施&#xff0c;众多中国…

04-react基础知识-路由

一、react路由环境安装 使用指令&#xff1a;npm i --save react-router-dom type/react-router-dom进行react路由环境安装 二、引入路由 在main.jsx文件中引入该语句&#xff1a; import { createBrowserRouter, RouterProvider } from react-router-dom 定义一个变量rou…

新登录接口独立版变现宝升级版知识付费小程序-多领域素材资源知识变现营销系统

源码简介&#xff1a; 资源入口 点击进入 源码亲测无bug&#xff0c;含前后端源码&#xff0c;非线传&#xff0c;修复最新登录接口 梦想贩卖机升级版&#xff0c;变现宝吸取了资源变现类产品的很多优点&#xff0c;摒弃了那些无关紧要的东西&#xff0c;使本产品在运营和变现…