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模板
摘要 随着人们生活水平的不断提高,私家车的数量正在逐年攀升。这带动了汽车维修行业的发展。越来越多的汽车维修厂如雨后春笋般涌现。同时,维修厂的业务操作产生了庞大的数据,这给汽车维修厂工作人员的数据管理提出了新的要求,他们…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
