WPF 数据验证
WPF提供了能与数据绑定系统紧密协作的验证功能。提供了两种方法用于捕获非法值:
1、可在数据对象中引发错误。
可以在设置属性时抛出异常,通常WPF会忽略所有在设置属性时抛出的异常,但可以进行配置,从而显示更有帮助的可视化指示。另一种选择是在自定义的数据类中实现 INotifyDataErrorInfo 或 IDataErrorInfo 接口,从而可得到指示错误的功能而不会抛出异常。
2、可在绑定级别上定义验证。
这种方法可获得使用相同验证的灵活性,而不必考虑使用的是哪个控件。更好的是,因为是在不同类中定义验证,可以很容易的在存储类似数据类型的多个绑定中重用验证。
错误模板
错误模板使用的是装饰层,装饰层是位于普通窗口内容之上的绘图层。使用装饰层,可添加可视化装饰来指示错误,而不用替换控件背后的控件模板或改变窗口的布局。文本框的标准错误模板通过在相应文本框的上面添加红色的Border元素来指示发生了错误。可使用错误模板添加其他细节。
<Style TargetType="{x:Type TextBox}"><Setter Property="Validation.ErrorTemplate"><Setter.Value><ControlTemplate><DockPanel LastChildFill="True"><TextBlock DockPanel.Dock="Right" Foreground="Red" FontSize="14" FontWeight="Bold" ToolTip="{Binding ElementName=adornerPlaceholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">*</TextBlock><Border BorderBrush="Green" BorderThickness="1"><AdornedElementPlaceholder Name="adornerPlaceholder"></AdornedElementPlaceholder></Border></DockPanel></ControlTemplate></Setter.Value></Setter><Style.Triggers><Trigger Property="Validation.HasError" Value="true"><Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/></Trigger></Style.Triggers>
</Style>
在数据对象中引发错误
我这里分别尝试了 IDataErrorInfo 与 INotifyDataErrorInfo 接口,这俩需要分别对应使用 <DataErrorValidationRule/> 与 <NotifyDataErrorValidationRule/> 。
IDataErrorInfo 接口定义了Error字段,这个字段在结构有多个字段时,难以映射,于是在[]下标下实现逻辑。
INotifyDataErrorInfo 接口需要实现 HasError,GetErrors 两个函数,并需要在属性的set 访问器内实现校验逻辑,并管理Error信息。
总体来说,在数据对象中引发错误不是一个好选择,验证逻辑硬编码在数据对象中,不利于复用。
在绑定级别上定义验证
在绑定级别上定义验证是针对于数据类型自定义验证规则,可以对同一种数据类型进行复用。自定义验证规则需要继承自 ValidationRule 类,需要实现 Validate 函数,在其中完成自定义验证内容。
public class PriceRule : ValidationRule
{private decimal min = 0;private decimal max = decimal.MaxValue;public decimal Min{get => min;set => min = value;}public decimal Max{get => max;set => max = value;}public override ValidationResult Validate(object value, CultureInfo cultureInfo){decimal price = 0;try{if (((string)value).Length > 0){price = Decimal.Parse((string)value, NumberStyles.Any, cultureInfo);}}catch{return new ValidationResult(false, "Illegal characters.");}if (price < min || price > max){return new ValidationResult(false, "Not in the range " + Min + " to " + Max + ".");}else{return new ValidationResult(true, null);}}
}
验证多个值
如果需要执行对两个或更多个绑定值的验证,可以通过 BindingGroup 来实现,将需要校验的多个控件放置于同一个容器中,在容器级别应用验证规则,需要通过事件主动触发验证,通常是子组件失去焦点时。
<Grid Grid.Row="3" Grid.ColumnSpan="2" DataContext="{Binding Path=Person}" TextBoxBase.LostFocus="Grid_LostFocus"><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Grid.BindingGroup><BindingGroup x:Name="personValidation"><BindingGroup.ValidationRules><local:PersonRule/></BindingGroup.ValidationRules></BindingGroup></Grid.BindingGroup><TextBlock Grid.Row="0" Grid.Column="0">Person ID:</TextBlock><TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=ID}" /><TextBlock Grid.Row="1" Grid.Column="0">Person Name:</TextBlock><TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Name}"/>
</Grid>
public class Person : ViewModelBase
{private string name = string.Empty;public string Name { get=>name; set=> SetProperty(ref name, value); }private string id = string.Empty;public string ID { get => id; set => SetProperty(ref id, value); }
}
public class PersonRule : ValidationRule
{public override ValidationResult Validate(object value, CultureInfo cultureInfo){BindingGroup bindingGroup = (BindingGroup)value;Person? person = bindingGroup.Items[0] as Person;string name = (string)bindingGroup.GetValue(person, "Name");string id = (string)bindingGroup.GetValue(person, "ID");if ((name == "") && (id == "")){return new ValidationResult(false, "A Person requires a ID or Name.");}else{return new ValidationResult(true, null);}}
}
下面贴出完整的测试代码:
MainWindow.xaml
<Window x:Class="TestValidation.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:TestValidation"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Border Padding="7" Margin="7" Background="LightSteelBlue"><Grid x:Name="myGrid"><Grid.Resources><Style TargetType="{x:Type TextBox}"><Setter Property="Validation.ErrorTemplate"><Setter.Value><ControlTemplate><DockPanel LastChildFill="True"><TextBlock DockPanel.Dock="Right" Foreground="Red" FontSize="14" FontWeight="Bold" ToolTip="{Binding ElementName=adornerPlaceholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">*</TextBlock><Border BorderBrush="Green" BorderThickness="1"><AdornedElementPlaceholder Name="adornerPlaceholder"></AdornedElementPlaceholder></Border></DockPanel></ControlTemplate></Setter.Value></Setter><Style.Triggers><Trigger Property="Validation.HasError" Value="true"><Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/></Trigger></Style.Triggers></Style></Grid.Resources><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition Height="*"/><RowDefinition Height="*"/><RowDefinition Height="*"/><RowDefinition Height="2*"/></Grid.RowDefinitions><TextBlock Grid.Row="0" Grid.Column="0">PersonAge:</TextBlock><TextBox Grid.Row="0" Grid.Column="1" DataContext="{Binding Path=PersonAge}"><TextBox.Text><Binding Path="Age" NotifyOnValidationError="true"><Binding.ValidationRules><DataErrorValidationRule/></Binding.ValidationRules></Binding></TextBox.Text></TextBox><TextBlock Grid.Row="1" Grid.Column="0">PersonName:</TextBlock><TextBox Grid.Row="1" Grid.Column="1" DataContext="{Binding Path=PersonName}"><TextBox.Text><Binding Path="Name" NotifyOnValidationError="true"><Binding.ValidationRules><NotifyDataErrorValidationRule/></Binding.ValidationRules></Binding></TextBox.Text></TextBox><TextBlock Grid.Row="2" Grid.Column="0">PersonPrice:</TextBlock><TextBox Grid.Row="2" Grid.Column="1" DataContext="{Binding Path=PersonPrice}"><TextBox.Text><Binding Path="Price" NotifyOnValidationError="true"><Binding.ValidationRules><local:PriceRule Min="0"/></Binding.ValidationRules></Binding></TextBox.Text></TextBox><Grid Grid.Row="3" Grid.ColumnSpan="2" DataContext="{Binding Path=Person}" TextBoxBase.LostFocus="Grid_LostFocus"><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Grid.BindingGroup><BindingGroup x:Name="personValidation"><BindingGroup.ValidationRules><local:PersonRule/></BindingGroup.ValidationRules></BindingGroup></Grid.BindingGroup><TextBlock Grid.Row="0" Grid.Column="0">Person ID:</TextBlock><TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=ID}" /><TextBlock Grid.Row="1" Grid.Column="0">Person Name:</TextBlock><TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=Name}"/></Grid></Grid></Border>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
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.Navigation;
using System.Windows.Shapes;namespace TestValidation;public class ViewModelBase : INotifyPropertyChanged
{public event PropertyChangedEventHandler? PropertyChanged;protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}protected virtual bool SetProperty<T>(ref T member, T value, [CallerMemberName] string? propertyName = null){if (EqualityComparer<T>.Default.Equals(member, value)){return false;}member = value;OnPropertyChanged(propertyName);return true;}
}
public class PersonAge : ViewModelBase, IDataErrorInfo
{public string this[string columnName]{get{if(columnName == "Age"){if(Age < 0 || Age > 150){return "Age must not be less than 0 or greater than 150.";}}return null;}}private int age;public int Age { get => age; set => SetProperty(ref age, value); }public string Error => null;
}
public class PersonName : ViewModelBase, INotifyDataErrorInfo
{private string name = string.Empty;public string Name {get => name;set{bool valid = true;foreach (char c in value){if (!char.IsLetterOrDigit(c)){valid = false;break;}}if(!valid){List<string> errors = new List<string> ();errors.Add("The Name can only contain letters and numbers.");SetErrors("Name", errors);}else{ClearErrors("Name");}SetProperty(ref name, value);}}private Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();private void SetErrors(string propertyName, List<string> propertyErrors){errors.Remove(propertyName);errors.Add(propertyName, propertyErrors);if (ErrorsChanged != null)ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));}private void ClearErrors(string propertyName){errors.Remove(propertyName);if(ErrorsChanged != null)ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));}public bool HasErrors{get { return errors.Count > 0; }}public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;public IEnumerable GetErrors(string? propertyName){if(!string.IsNullOrEmpty(propertyName) && errors.ContainsKey(propertyName)){return errors[propertyName];}return new List<string>();}
}
public class PersonPrice : ViewModelBase, INotifyPropertyChanged
{private decimal price = 0;public decimal Price{get => price; set{SetProperty(ref price, value);}}
}public class PriceRule : ValidationRule
{private decimal min = 0;private decimal max = decimal.MaxValue;public decimal Min{get => min;set => min = value;}public decimal Max{get => max;set => max = value;}public override ValidationResult Validate(object value, CultureInfo cultureInfo){decimal price = 0;try{if (((string)value).Length > 0){price = Decimal.Parse((string)value, NumberStyles.Any, cultureInfo);}}catch{return new ValidationResult(false, "Illegal characters.");}if (price < min || price > max){return new ValidationResult(false, "Not in the range " + Min + " to " + Max + ".");}else{return new ValidationResult(true, null);}}
}public class Person : ViewModelBase
{private string name = string.Empty;public string Name { get=>name; set=> SetProperty(ref name, value); }private string id = string.Empty;public string ID { get => id; set => SetProperty(ref id, value); }
}
public class PersonRule : ValidationRule
{public override ValidationResult Validate(object value, CultureInfo cultureInfo){BindingGroup bindingGroup = (BindingGroup)value;Person? person = bindingGroup.Items[0] as Person;string name = (string)bindingGroup.GetValue(person, "Name");string id = (string)bindingGroup.GetValue(person, "ID");if ((name == "") && (id == "")){return new ValidationResult(false, "A Person requires a ID or Name.");}else{return new ValidationResult(true, null);}}
}
public partial class MainWindow : Window
{public MainWindow(){InitializeComponent();myGrid.DataContext = this;}public PersonAge PersonAge { get; set; } = new PersonAge();public PersonName PersonName { get; set; } = new PersonName();public PersonPrice PersonPrice { get; set; } = new PersonPrice();public Person Person { get; set; } = new Person();private void Grid_LostFocus(object sender, RoutedEventArgs e){personValidation.CommitEdit();}
}
相关文章:
WPF 数据验证
WPF提供了能与数据绑定系统紧密协作的验证功能。提供了两种方法用于捕获非法值: 1、可在数据对象中引发错误。 可以在设置属性时抛出异常,通常WPF会忽略所有在设置属性时抛出的异常,但可以进行配置,从而显示更有帮助的可视化指示…...
IDEA的maven想显示层级关系,而非平级
新版和旧版的IDEA的位置不一样,2023.2.1的版本在右上角的“” 这个位置 如图所示: 然后点击按模块分组:...
(八)k8s实战-身份认证与权限
一、认证 User AccountsService Accounts Service Account 自动化: Service Account Admission ControllerToken ControllerService Account Controller 1、Service Account Admission Controller 通过 Admission Controller 插件来实现对 pod 修改,…...
数学建模:TOPSIS分析
🔆 文章首发于我的个人博客:欢迎大佬们来逛逛 TOPSIS分析法 算法流程 假设有m个评价对象,n个评价指标,首先需要进行指标的正向化: 极大型极小型单点型区间型 然后对正向化后的矩阵进行标准化,得到 Z Z Z…...
【Qt学习】10 利用QSharedMemory实现单例运行
问题 让应用程序只有一个运行实例 QSharedMemory除了可以完成进程间通信,还可以实现应用程序单例化。 解法 首先,看看QSharedMemory的几个函数: 1、QSharedMemory(const QString &key, QObject *parent Q_NULLPTR)构造函数 该构造函数…...
FPGA应用于图像处理
FPGA应用于图像处理 FPGA(Field-Programmable Gate Array)直译过来就是现场可编程门阵列。是一种可以编程的逻辑器件,具有高度的灵活性,可以根据具体需求就像编程来实现不同的功能。 FPGA器件属于专用的集成电流中的一种半定制电…...
vscode python 无法引入上层目录解决
在vscode 中.vscode 配置如下 { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid830387 “version”: “0.2.0”, “configurati…...
[开发|java] java list 取某个属性最大的项
示例代码: import java.util.*;class Person {private String name;private int age;public Person(String name, int age) {this.name name;this.age age;}public int getAge() {return age;} }public class Main {public static void main(String[] args) {List<Person…...
关闭浏览器的跨域校验
首发博客地址 问题描述 当你访问资源失败,并遇到以下类似提示时: Access to script at 资源路径 from origin null has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrom…...
USRP 简介,对于NI软件无线电你所需要了解的一切
什么是 USRP 通用软件无线电外设( USRP ) 是由 Ettus Research 及其母公司National Instruments设计和销售的一系列软件定义无线电。USRP 产品系列由Matt Ettus领导的团队开发,被研究实验室、大学和业余爱好者广泛使用。 大多数 USRP 通过以太网线连接到主机&…...
RTE_Driver驱动框架和Keil下开发需要支持的xxx_DFP软件包分析
1.RTE_Driver驱动框架 RTE_Driver代表"Run-Time Environment Driver",是Keil MDK(Microcontroller Development Kit)中的一个概念。Keil MDK是一种用于嵌入式系统开发的集成开发环境,提供了开发、编译、调试等一系列工具…...
ImportError: Cannot load dynamic library. Did you compile LSD?
1、问题描述 >>> import pylsd2 Traceback (most recent call last):File "<stdin>", line 1, in <module>File "/data/data/wangzy-p-wangzy-p3-volume-pvc-0fee40a7-7013-49b4-8cfb-b4ab0394165b/.conda/envs/paddle/lib/python3.8/sit…...
音频应用编程
目录 ALSA 概述alsa-lib 简介sound 设备节点alsa-lib 移植编写一个简单地alsa-lib 应用程序一些基本概念打开PCM 设备设置硬件参数读/写数据示例代码之PCM 播放示例代码值PCM 录音 使用异步方式PCM 播放示例-异步方式PCM 录音示例-异步方式 使用poll()函数使用poll I/O 多路复用…...
软件测试/测试开发丨Python 学习笔记 之 链表
点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接:https://ceshiren.com/t/topic/26458 链表与数组的区别 复杂度分析 时间复杂度数组链表插入删除O(n)O(1)随机访问O(1)O(n) 其他角度分析 内存连续,利用CPU的机制࿰…...
Matlab 使用经验分享(常用函数介绍;矩阵常见计算)
Matlab 使用经验分享 大家好!最近有很多朋友询问我关于 Matlab 的使用,于是我决定写一篇博客来分享一下我的经验。对于数学和编程爱好者来说,Matlab 是一个非常有用的工具。我自己在数学实验和数学建模竞赛中也经常使用它。那么,…...
软件工程(十七) 行为型设计模式(三)
1、观察者模式 简要说明 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新 速记关键字 联动,广播消息 类图如下 基于上面的类图,我们来实现一个监听器。类图中的Subject对应我们的被观察对象接口(IObservable),…...
在抖音中使用语聚AI,实现自动回复用户视频评论、私信问答
您可以通过集简云数据流程,将语聚AI助手集成到抖音视频评论、抖音私信,实现自动回复用户视频评论、私信问答,大大提升账号互动与运营效率。 效果如下: 自动化流程: ● 抖音普通号评论对接语聚AI(点击可一…...
pyqt5-快捷键QShortcut
import sys from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import *""" 下面示例揭示了,当关键字绑定的控件出现的时候,快捷键才管用, 绑定的控件没有出现的时候快捷键无效 """…...
匿名函数( lambda 表达式)
在 C 中,匿名函数也被称为 lambda 表达式。C11 引入了 lambda 表达式,使得在需要函数对象(函数符)的地方可以使用匿名函数来代替。 lambda 表达式的基本语法如下: [capture list] (parameter list) -> return typ…...
基于SSM的汽车维修管理系统——LW模板
摘要 随着人们生活水平的不断提高,私家车的数量正在逐年攀升。这带动了汽车维修行业的发展。越来越多的汽车维修厂如雨后春笋般涌现。同时,维修厂的业务操作产生了庞大的数据,这给汽车维修厂工作人员的数据管理提出了新的要求,他们…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
