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模板
摘要 随着人们生活水平的不断提高,私家车的数量正在逐年攀升。这带动了汽车维修行业的发展。越来越多的汽车维修厂如雨后春笋般涌现。同时,维修厂的业务操作产生了庞大的数据,这给汽车维修厂工作人员的数据管理提出了新的要求,他们…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...

软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...

Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...