一、前言
在自己的項目中挺多地方需要涉及到數據驗證的,初期的實現方式都是通過點擊確定后再逐個驗證數據是否符合要求,但這種方式會讓后台代碼變得很多很亂。於是就開始在網上需求好的解決方式,剛好看到了一個大佬的博客寫了關於數據驗證的博客,也成功將那個方法用在項目中了,今天就來這里分享一下。博客原文: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、到這里功能的實現就基本完成了,不擅長講解原理,不太懂得可以研究研究官方文檔獲取其他大佬的文章,實現效果如下: