输入框验证的方式有很多种,这里主要讲述我用到的几种方式。
MVVM基类:
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
实体基类:
internal class EntityBase : BindableBase
{
private ValidationObjectErrors _errors;
public ValidationObjectErrors Errors
{
get => _errors;
set { _errors = value; RaisePropertyChanged(); }
}
public EntityBase()
{
}
public override void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
base.RaisePropertyChanged(propertyName);
_errors = new ValidationObjectErrors(this);//触发校验
base.RaisePropertyChanged(nameof(Errors));
}
}
错误集ValidationObjectErrors:
public class ValidationObjectErrors : IReadOnlyList<string>
{
private List<string> _messages = new List<string>();
private List<ValidationResult> _results = new List<ValidationResult>();
internal ValidationObjectErrors(EntityBase entityBase)
{
ValidationContext context = new ValidationContext(entityBase);
Validator.TryValidateObject(entityBase, context, _results, true);//校验
foreach (ValidationResult result in _results)
{
_messages.Add(result.ErrorMessage);
}
}
public int Count => _messages.Count;
public string this[int index] => _messages[index];
public string this[string propertyName]
{
get
{
foreach (ValidationResult result in _results)
{
if (result.MemberNames.Contains(propertyName))
{
return result.ErrorMessage;
}
}
return null;
}
}
public IEnumerator<string> GetEnumerator()
{
return _messages.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
实体类:
internal class PersonInfo : EntityBase
{
private string _no;
[StringLength(20, MinimumLength = 6, ErrorMessage = "最少 6 位,最长 20 位")]
public string No
{
get => _no;
set { _no = value; RaisePropertyChanged(); }
}
private string _name;
//[String(IsAllowedNull = false, MaxLenth = 10)]//自定义的
public string Name
{
get => _name;
set { _name = value; RaisePropertyChanged(); }
}
private int _age;
[Range(10, 100, ErrorMessage = "10岁-100岁")]
public int Age
{
get => _age;
set { _age = value; RaisePropertyChanged(); }
}
public string Height { get; set; }
public string Weight { get; set; }
}
MainWindowViewModel:
internal class MainWindowViewModel
{
public MainWindowViewModel()
{
PersonInfo = new PersonInfo();
}
public PersonInfo PersonInfo { get; set; }
}
界面MainWindow:
可直接绑定属性种的errors集,显示错误提示
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Style.Setters>
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="Auto"/>
<Setter Property="Margin" Value="0,0,5,0"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style.Setters>
</Style>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Style.Setters>
<Setter Property="Width" Value="150"/>
<Setter Property="Height" Value="30"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Border Width="250" Height="350" BorderThickness="2" BorderBrush="Black">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="编号:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding PersonInfo.No, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<TextBlock Text="{Binding Path =PersonInfo.Errors[No]}" Foreground="Red"/>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="姓名:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding PersonInfo.Name, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<TextBlock Text="{Binding Path =PersonInfo.Errors[Name]}" Foreground="Red"/>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="年龄:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding PersonInfo.Age, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<TextBlock Text="{Binding Path =PersonInfo.Errors[Age]}" Foreground="Red"/>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="身高:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding PersonInfo.Height, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<TextBlock Text="{Binding Path =PersonInfo.Errors[Height]}" Foreground="Red"/>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="体重:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding PersonInfo.Weight, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<TextBlock Text="{Binding Path =PersonInfo.Errors[Weight]}" Foreground="Red"/>
<Button Height="30" Width="60" Content="保存"/>
</StackPanel>
</Border>
</Grid>
当然可以自定义约束特性:
[AttributeUsage(AttributeTargets.Property)]
public class StringAttribute : ValidationAttribute
{
///
/// 最小长度
///
public int MinLenth { get; set; }
///
/// 最大长度
///
public int MaxLenth { get; set; }
///
/// 是否允许为空
///
public bool IsAllowedNull { get; set; }
///
/// 是否允许有中文
///
public bool IsAllowedChinese { get; set; }
///
/// 是否允许有空格
///
public bool IsAllowedSpace { get; set; }
public StringAttribute()
: base()
{
}
public override string FormatErrorMessage(string name)
{
return string.Format(CultureInfo.CurrentUICulture, ErrorMessageString, name, ErrorMessage);
}
public override bool IsValid(object value)
{
string valueString = value?.ToString();
if (!string.IsNullOrEmpty(valueString))
{
if (valueString.Length < MinLenth || valueString.Length > MaxLenth)
{
ErrorMessage = "字符串长度" + MinLenth + "-" + MaxLenth;
return false;
}
}
if (!IsAllowedNull)
{
if (string.IsNullOrEmpty(valueString))
{
ErrorMessage = "不能为空!";
return false;
}
}
if (!IsAllowedChinese)
{
if (Regex.IsMatch(valueString, @"[\u4e00-\u9fa5]"))
{
ErrorMessage = "不能包含中文!";
return false;
}
}
if (!IsAllowedSpace)
{
if (valueString.Contains(" "))
{
ErrorMessage = "不能包含空格!";
return false;
}
}
return true;
}
}
结果展示:
源代码:https://download.csdn.net/download/weixin_48083386/85406762
这个比较简单,直接上代码:
ViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
private int _age;
public int Age
{
get => _age;
set
{
_age = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Age)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
界面:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Style.Setters>
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="Auto"/>
<Setter Property="Margin" Value="0,0,5,0"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style.Setters>
</Style>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Style.Setters>
<Setter Property="Width" Value="150"/>
<Setter Property="Height" Value="30"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Border Width="250" Height="150" BorderThickness="2" BorderBrush="Black">
<StackPanel Margin="10">
<TextBlock Text="{Binding Path =PersonInfo.Errors[No]}" Foreground="Red"/>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="姓名:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Name="NameTb" ToolTip="{Binding RelativeSource={x:Static RelativeSource.Self},Path=(Validation.Errors)[0].ErrorContent}">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True" >
<Binding.ValidationRules>
<local:CustomValidationRule ValidateType="Name"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
<TextBlock Text="{Binding ElementName=NameTb,Path=(Validation.Errors)[0].ErrorContent}" Foreground="Red"/>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="年龄:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Name="AgeTb" ToolTip="{Binding RelativeSource={x:Static RelativeSource.Self},Path=(Validation.Errors)[0].ErrorContent}">
<TextBox.Text>
<Binding Path="Age" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True" >
<Binding.ValidationRules>
<local:CustomValidationRule ValidateType="Age"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
<TextBlock Text="{Binding ElementName=AgeTb,Path=(Validation.Errors)[0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</Border>
</Grid>
自定义规则:
class CustomValidationRule : ValidationRule
{
public string ValidateType { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
try
{
string valueString = value?.ToString().Trim();
if (ValidateType == "Name")//姓名
{
string name = valueString;
if (!string.IsNullOrEmpty(name))
{
string errorMessage = string.Empty;
return name.Length > 10 ? new ValidationResult(false, "名称过长,名称长度不能超过10") : new ValidationResult(true,null);
}
}
else if (ValidateType == "Age")//年龄
{
string ageString = valueString;
if (!string.IsNullOrEmpty(ageString))
{
if (int.TryParse(ageString, out int age))
{
if (age is < 10 or > 150)
{
return new ValidationResult(false, "年龄不能小于10岁,也不能大于150岁");
}
}
return new ValidationResult(true, null);
}
}
return new ValidationResult(true, null);
}
catch (Exception e)
{
return new ValidationResult(false, e.Message);
}
}
}
其实.Net中的数据验证方法有很多,可以参考这篇:WPF数据验证
这里写一个不一样的,FluentValidation。可以直接在NuGet中下载。
首先还是View和ViewModel:
class MainWindowViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
MainWindowValid();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
private int _age;
public int Age
{
get => _age;
set
{
_age = value;
MainWindowValid();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Age)));
}
}
private string _errorMessage;
public string ErrorMessage
{
get => _errorMessage;
set
{
_errorMessage = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ErrorMessage)));
}
}
private ValidationFailure MainWindowValid()
{
ValidationFailure failure = ValidatorHelper.GetFirstError<MainWindowValidator, MainWindowViewModel>(this);
// 获取校验失败的提示以及属性名称
if (failure != null)
{
ErrorMessage = failure.ErrorMessage;
}
return failure;
}
public event PropertyChangedEventHandler PropertyChanged;
}
界面:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Style.Setters>
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="Auto"/>
<Setter Property="Margin" Value="0,0,5,0"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style.Setters>
</Style>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Style.Setters>
<Setter Property="Width" Value="150"/>
<Setter Property="Height" Value="30"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Border Width="250" Height="150" BorderThickness="2" BorderBrush="Black">
<StackPanel Margin="10">
<TextBlock Text="{Binding Path =PersonInfo.Errors[No]}" Foreground="Red"/>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="姓名:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Name="NameTb" Text="{Binding Name, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="年龄:" Style="{StaticResource TextBlockStyle}"/>
<TextBox Style="{StaticResource TextBoxStyle}" Name="AgeTb" Text="{Binding Age, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
<TextBlock Text="{Binding ErrorMessage}" Foreground="Red"/>
</StackPanel>
</Border>
</Grid>
校验器:
class MainWindowValidator : AbstractValidator<MainWindowViewModel>
{
public MainWindowValidator()
{
RuleFor(x => x.Name)
.NotNull().WithMessage("请输入姓名!")
.MinimumLength(5).WithMessage("姓名长度不能小于5!")
.MaximumLength(50).WithMessage("姓名长度不能大于50!");
RuleFor(x => x.Age)
.NotNull().WithMessage("请输入年龄!")
.Must(IsBetween10And150).WithMessage("年龄在范围10-150岁之间!");
}
public bool IsBetween10And150(int age)
{
return age is >= 10 and <= 150;
}
}
帮助类ValidatorHelper:
///
/// 实体验证
///
public static class ValidatorHelper
{
///
/// 验证,抛出异常信息
///
/// 验证器
/// 待验证实体
///
public static void ValidateMsg<TValidator, TDto>(TDto dto) where TValidator : AbstractValidator<TDto>, new()
{
TValidator validator = new TValidator();
ValidationResult validationResult = validator.Validate(dto);
if (!validationResult.IsValid)
{
throw new Exception(validationResult.Errors.FirstOrDefault().ErrorMessage);
}
}
///
/// 验证,抛出异常信息
///
/// 验证器
/// 待验证实体
///
public static void ValidateMsgs<TValidator, TDto>(TDto dto) where TValidator : AbstractValidator<TDto>, new()
{
TValidator validator = new TValidator();
ValidationResult validationResult = validator.Validate(dto);
if (!validationResult.IsValid)
{
var msgs = string.Join(";\r\n", validationResult.Errors.Select(a => a.ErrorMessage));
throw new Exception(msgs);
}
}
public static ValidationFailure GetFirstError<TValidator, TDto>(TDto dto) where TValidator : AbstractValidator<TDto>, new()
{
TValidator validator = new TValidator();
ValidationResult result = validator.Validate(dto);
if(!result.IsValid)
{
return result.Errors[0];
}
else
{
return null;
}
}
在.Net中校验的方法有很多种,具体看业务需求。