TrykleMvvmToolkit.Wpf 模型验证
介绍
本验证可以使用3个方式进行操作
- 2 使用
System.ComponentModel.DataAnnotations特性验证 - 3 在构造函数中使用
SetRule设置额外规则 - 1 添加事件
Errors.Validating可以调用其他库进行验证
这三种验证方式,按照123顺序执行,其中任意一个出现错误都将直接短路返回
使用
需要验证的 model 从 ValidationViewModelBase 继承
public class User : ValidationViewModelBase
{
[Required(ErrorMessage = "请填写邮箱")]
[EmailAddress(ErrorMessage = "邮箱格式错误")]
public string Email { get => GetProperty<string>(); set => SetPropertyAndValidate(value); }
触发一个属性的验证
在属性绑定到UI时,属性改变将通知验证,如验证出现错误,验证信息将保存并通知到基类的Error属性中,可以使用Errors[属性名]来访问具体的错误信息
转换器ObjectToVisibilityConverter在此处的作用是将非空文本转换为可见性
<StackPanel Margin="10">
<TextBox Height="26" Text="{Binding Path=User.Email, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Foreground="Red" Text="{Binding Path=User.Errors[Email]}"
Visibility="{Binding Path=User.Errors[Email],
Converter={t:ObjectToVisibilityConverter FalseToCollapsed=True}}" />
</StackPanel>
全部验证
一个Modal中往往有很多属性需要验证,根据上方触发验证的设计,你只能手动更改所有属性来触发所有属性验证
使用 Errors.ValidateAll() 可以一次性验证全部(这样你会看到UI上显示一堆提示)
Errors.ValidateAll() 可以用在按钮提交事件内进行验证,或者初始时使用Errors.ValidateAll()之后使用Errors.Count用于按钮启用绑定
Error.Count 中储存了错误数量
Error.Messages 中储存了错误信息字符串集合
收集常用验证规则
//字段非空,最大长度160
[Required(ErrorMessage="FirstName不能为空")]
[StringLength(160, ErrorMessage = "FirstName太长了")]
[StringLength(32, MinimumLength = 6, ErrorMessage = "密码长度应在{2}-{1}之间")]
public string FirstName { get; set; }
//正则表达式验证必须是电子邮件
[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
ErrorMessage="邮件地址不正确")]
public string Email { get; set; }
//字段非空,数值类型最小值,最大值
[Required]
[Range(typeof(decimal), "0.00", "49.99")]
public decimal Total { get; set; }
//对象两个属性拥有相同的值
[Compare("Email")]
public string EmailConfirm { get; set; }
使用SetRule方法设置验证规则
SetRule(() => Email, () => Email?[0] == 'A', "邮箱第一个字母必须以A开头");
用抛出异常的方式传递错误消息
SetRule(() => Email, () =>
{
ThrowErrorIf(Email.Length != 12, $"邮箱长度必须12,当前({Email?.Length})");
});
使用Validating事件设置验证规则
Errors.Validating += e =>
{
if (e.Name == nameof(Email))
{
if ($"{e.Input}".Length > 10)
{
e.Failed("邮箱长度要小于10,如果有其他验证的库需要接入,可以在这里使用ReturnError中断内部验证并返回失败");
}
}
};
补充
通过给Errors对象添加Validating事件,可以定制额外的拦截步骤
通过给Errors对象添加Validated事件,可以获得验证后的回调
注意:Validated事件内,不要对验证的属性赋值,将造成调用溢出(已修改为抛出异常作为拦截)
数字属性绑定到字符串属性的弊端和解决方案
前台Binding的错误无法传播到模型来,也就无法进行验证,
比如TextBox上绑定int类型的属性,在输入的文本无法转换为int时,会触发绑定出错,值不会传递到模型中
你可以通过限制控件本身的输入来将一个默认值传递到模型,这样便会触发验证
或者,使用一个转换器来阻止默认的绑定报错
FallbackValue可设置转换错误时返回的兜底值
<TextBox Height="26" Text="{Binding User.Age, Converter={t:StringToNumberConverter FallbackValue=-1}}" />
单独使用ValidationObject
对于不能继承ValidationViewModelBase的情况,可以单独使用
public class Person
{
[Required(ErrorMessage = "名字不能为空")]
public string Name { get; set; }
[Range(typeof(int), "1", "99", ErrorMessage = "年龄必须要在{1}和{2}之间")]
public int Age { get; set; }
}
internal class Program
{
[STAThread]
static void Main(string[] args)
{
var p = new Person();
var vo = new ValidationObject<Person>(p);
//额外规则添加
vo.AddRule(x => x.Age, x => x.Age < 9, "年龄必须大于9岁");
vo.AddRule(x => x.Name, x => vo.ThrowErrorIf(string.IsNullOrEmpty(x.Name), "名字不可为空"));
vo.AddRule(
x => x.Name,
x =>
{
return string.IsNullOrWhiteSpace(x.Name);
},
"请填写Name"
);
//验证全部
vo.ValidateAll();
Console.WriteLine("Count:" + vo.Count);
foreach (var item in vo.Messages)
{
Console.WriteLine(item);
}
//验证部分
if (!vo.Validate(nameof(Person.Name), out var errMsg))
{
Console.WriteLine($"-->{errMsg}");
}
Console.Read();
}
}
浙公网安备 33010602011771号