WPF开发随笔收录-DataAnnotations实现数据校验(MVVM架构下)
一、前言
在自己的项目中挺多地方需要涉及到数据验证的,初期的实现方式都是通过点击确定后再逐个验证数据是否符合要求,但这种方式会让后台代码变得很多很乱。于是就开始在网上需求好的解决方式,刚好看到了一个大佬的博客写了关于数据验证的博客,也成功将那个方法用在项目中了,今天就来这里分享一下。博客原文:https://www.cnblogs.com/wzh2010/p/6518834.html
二、正文
1、这里写一个简单的例子来作为参考,首先新建个项目,按照常规MVVM构建好项目,如下图。然后添加需要用到的通知基类和命令基类

2、然后就是添加一个验证的基类ValidateModelBase,后面需要实现验证的类直接继承这个类即可,这个代码是直接搬大佬的
public class ValidateModelBase : NotifyBase, IDataErrorInfo
{
public ValidateModelBase()
{ } #region 属性
/// <summary>
/// 表当验证错误集合
/// </summary>
public Dictionary<string, string> dataErrors = new Dictionary<string, string>(); /// <summary>
/// 是否验证通过
/// </summary>
public bool IsValidated
{
get
{
if (dataErrors != null && dataErrors.Count > 0)
{
return false;
}
return true;
}
}
#endregion public string this[string columnName]
{
get
{
ValidationContext vc = new ValidationContext(this, null, null);
vc.MemberName = columnName;
var res = new List<ValidationResult>();
var result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, res);
if (res.Count > 0)
{
string errorInfo = string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
AddDic(dataErrors, columnName, errorInfo);
return errorInfo;
}
RemoveDic(dataErrors, columnName);
return null;
}
} public string Error
{
get
{
return null;
}
} #region 附属方法
/// <summary>
/// 移除字典
/// </summary>
/// <param name="dics"></param>
/// <param name="dicKey"></param>
private void RemoveDic(Dictionary<string, string> dics, string dicKey)
{
dics.Remove(dicKey);
} /// <summary>
/// 添加字典
/// </summary>
/// <param name="dics"></param>
/// <param name="dicKey"></param>
private void AddDic(Dictionary<string, string> dics, string dicKey, string dicValue)
{
if (!dics.ContainsKey(dicKey)) dics.Add(dicKey, dicValue);
}
#endregion
}
3、接着我们创建一个类,并添加需要验证的字段
public class UserModel : ValidateModelBase
{
private string userName;
[Required(ErrorMessage = "用户名不可为空")]
public string UserName
{
get { return userName; }
set { userName = value; DoNotify(); }
} private string userAge;
[Required(ErrorMessage = "年龄不可为空")]
[Range(0, 100, ErrorMessage = "年龄范围为0~100")]
public string UserAge
{
get { return userAge; }
set { userAge = value; DoNotify(); }
} private bool isFormValid; public bool IsFormValid
{
get { return isFormValid; }
set { isFormValid = value; DoNotify(); }
}
}
4、重写一下TextBox的Validation.ErrorTemplate模板
<Style
x:Key="{x:Type TextBoxBase}"
BasedOn="{x:Null}"
TargetType="{x:Type TextBoxBase}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="2,1,1,1" />
<Setter Property="AllowDrop" Value="true" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="SelectionBrush" Value="{DynamicResource Accent}" />
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Vertical">
<Border
x:Name="adornerborder"
BorderThickness="1">
<Grid>
<AdornedElementPlaceholder x:Name="adorner" Margin="-1" />
</Grid>
</Border>
<Border
x:Name="errorBorder"
Background="Transparent"
CornerRadius="0"
IsHitTestVisible="False"
Opacity="0">
<TextBlock
VerticalAlignment="Center"
Foreground="Red"
Text="{Binding ElementName=adorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
</Border>
</StackPanel> <ControlTemplate.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding ElementName="adorner" Path="AdornedElement.Tag" />
</DataTrigger.Binding>
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="fadeInStoryboard1">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="errorBorder"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="00:00:00.15" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.Setters>
<Setter TargetName="adornerborder" Property="BorderBrush" Value="#FFdc000c" />
</DataTrigger.Setters>
</DataTrigger> <DataTrigger Value="True">
<DataTrigger.Binding>
<Binding ElementName="adorner" Path="AdornedElement.IsKeyboardFocused" />
</DataTrigger.Binding>
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="fadeInStoryboard">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="errorBorder"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="00:00:00.15" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.Setters>
<Setter TargetName="adornerborder" Property="BorderBrush" Value="#FFdc000c" />
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
5、接着在主窗口添加两个TextBox,然后绑定上对应的字段,记得要加上这个ValidatesOnDataErrors=True,不然无效
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Margin="0,10" Orientation="Horizontal">
<Label
Width="80"
Content="用户姓名:" />
<TextBox
Width="150"
Tag="{Binding UserModel.IsFormValid, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding UserModel.UserName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
</StackPanel>
<StackPanel Margin="0,10" Orientation="Horizontal">
<Label
Width="80"
Content="用户年龄:" />
<TextBox
Width="150"
Tag="{Binding UserModel.IsFormValid, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding UserModel.UserAge, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
</StackPanel> <Button
Margin="0,10,0,0"
Command="{Binding SubmitCommand}"
Content="提交" />
</StackPanel>
</Grid>
6、然后就是ViewModel的代码
public class MainWindowViewModel : NotifyBase
{
private UserModel userModel; public UserModel UserModel
{
get { return userModel; }
set { userModel = value; DoNotify(); }
} public CommandBase SubmitCommand { get; set; } = new CommandBase(); public MainWindowViewModel()
{
UserModel = new UserModel(); SubmitCommand.DoExecute = new Action<object>(DoSumit);
SubmitCommand.DoCanExecute = new Func<object, bool>((o) => { return true; });
} private void DoSumit(object obj)
{
if (UserModel.IsValidated) MessageBox.Show("验证通过!");
else
{
UserModel.IsFormValid = true;
var dataErrors = UserModel.dataErrors;
MessageBox.Show("验证失败");
}
}
}
7、到这里功能的实现就基本完成了,不擅长讲解原理,不太懂得可以研究研究官方文档获取其他大佬的文章,实现效果如下:

WPF开发随笔收录-DataAnnotations实现数据校验(MVVM架构下)的更多相关文章
- WPF开发随笔收录-ScrollViewer滑块太小解决方案
一.前言 在WPF开发过程中,ScrollViewer是一个很常使用到的控件,在自己工作的项目中,收到一个反馈就是当ScrollViewer里面的内容太长时,滚动条的滑块就会变得很小,然后导致点击起来 ...
- WPF开发随笔收录-DrawingVisual绘制高性能曲线图
一.前言 项目中涉及到了心率监测,而且数据量达到了百万级别,通过WPF实现大数据曲线图时,尝试过最基础的Canvas来实现,但是性能堪忧,而且全部画出来也不实际.同时也尝试过找第三方的开源库,但是因为 ...
- WPF开发随笔收录-仿安卓Toast
一.前言 在项目中,经常需要用到消息提醒功能,在以前接触安卓开发那会使用过Toast,于是打算在WPF上也来模仿一个,话不多说,撸起袖子干起来! 二.正文 1.首先新建一个工程,工程的目录如下 2.编 ...
- WPF开发随笔收录-心电图曲线绘制
一.前言 项目中之前涉及到胎儿心率图曲线的绘制,最近项目中还需要添加心电曲线和血样曲线的绘制功能.今天就来分享一下心电曲线的绘制方式: 二.正文 1.胎儿心率曲线的绘制是通过DrawingVisual ...
- WPF开发随笔收录-唯一标识符GUID
一.前言 该系列博客用于记录本人在WPF开发过程中遇到的各种知识点 二.正文 1.在工作的项目中,软件需要用到在线升级功能,由于第一次弄,在下载服务端的文件到本地时,文件的名称我选择直接生成为固定的格 ...
- WPF开发随笔收录-WriteableBitmap绘制高性能曲线图
一.前言 之前分享过一期关于DrawingVisual来绘制高性能曲线的博客,今天再分享一篇通过另一种方式来绘制高性能曲线的方法,也就是通过WriteableBitmap的方式:具体的一些细节这里就不 ...
- WPF开发随笔收录-获取软件当前目录的坑
一.唠唠叨叨 软件开发过程中,经常需要使用到获取exe当前目录这个功能,前同事在实现这个需求时使用的是Directory.GetCurrentDirectory()这个方法,但再最近的测试中,突然发现 ...
- WPF开发随笔收录-报警闪烁效果实现
一.前言 工作中目前经手的项目是医疗相关的监护软件,所以会涉及到一些报警效果的实现,今天在这里就简单分享一下实现方式 二.正文 1.实现的方式比较的简单,就是通过一个Border控件,然后搭配Data ...
- WPF开发随笔收录-带递增递减按钮TextBox
一.前言 今天分享一下如何实现带递增递减按钮的TextBox控件 二.正文 1.之前的博客分享了一篇自定义XamlIcon控件的文章,这次就直接在那个项目的基础上实现今天这个自定义控件 2.首先添加两 ...
随机推荐
- Bootstrap Blazor Table 组件(二)
原文链接:https://www.cnblogs.com/ysmc/p/16128206.html 很多小伙伴在使用 Bootstrap Blazor Table组件的时候,都会有这样的一个需求: 我 ...
- python爬虫---字体反爬
目标地址:http://glidedsky.com/level/web/crawler-font-puzzle-1 打开google调试工具检查发现网页上和源码之中的数字不一样, 已经确认该题目为 字 ...
- 2021.11.09 P3435 [POI2006]OKR-Periods of Words(KMP)
2021.11.09 P3435 [POI2006]OKR-Periods of Words(KMP) https://www.luogu.com.cn/problem/P3435 题意: 对于一个仅 ...
- Halo 开源项目学习(二):实体类与数据表
基本介绍 Halo 项目中定义了一些实体类,用于存储博客中的关键数据,如用户信息.文章信息等.在深入学习 Halo 的设计理念与实现过程之前,不妨先学习一下一个完整的博客系统都由哪些元素组成. 实体类 ...
- jmeter元件,作用域与优先级
jmeter元件,作用域与优先级 一.jmeter元件 1.配置元件:优先级最高 1.1 重点使用元件:csv数据文件设置.用户定义变量.计数器 2.取样器:根据不同协议来编写请求脚本的元件 2.1 ...
- SpringBoot扩展点EnvironmentPostProcessor
一.背景 之前项目中用到了Apollo配置中心,对接Apollo配置中心后,配置中心的属性就可以在程序中使用了,那么这个是怎么实现的呢?配置中心的属性又是何时加载到程序中的呢?那么我们如果找到了这个是 ...
- 微信小程序订阅消息
概述 消息能力是小程序能力中的重要组成,我们为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验. 订阅消息推送位置:服务通知 订阅消息下发条件:用户自主订阅 订阅消息卡片跳转能力:点击查看详情 ...
- 变量命名 函数命名 方法 Naming cheatsheet
Naming things is hard. This sheet attempts to make it easier. Although these suggestions can be appl ...
- vscode无法运行和调试使用了部分stl库的程序(无法定位程序输入点__gxx_personality_v0的一个解决方法)
一.起因 vscode 不能运行带有部分 stl 库的程序,编译不会报错,运行也不会报错但是也没有结果,调试的话会有下图中报错,如果没有string或者vector一切正常. 二.分析 cmd 中运 ...
- Java 15 新特性:文本块
大家好,我是DD,今天继续来学点Java的新特性! 假设有这样一个场景,我们需要做一个工具.用来自动生成项目文档,文档可以通过浏览器查看,所以最后产出物肯定是一堆html文件.为了让这些html文件更 ...