• WPF开发随笔收录DataAnnotations实现数据校验(MVVM架构下)


    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、到这里功能的实现就基本完成了,不擅长讲解原理,不太懂得可以研究研究官方文档获取其他大佬的文章,实现效果如下:

  • 相关阅读:
    用kettle做ETL时设置mysql连接参数使数据写入速度加快
    infobright社区版rpm包
    mysql在大数据量下性能调优相关参数
    greenplum给某个用户赋予整个schema下所有表的权限
    Linux挂载大于2T的磁盘硬盘
    Centos 系统swap(虚拟内存)管理
    域内
    随便记录一些东西
    有关终端的一些tips
    精悍的指令
  • 原文地址:https://www.cnblogs.com/sexintercourse/p/16414509.html
Copyright © 2020-2023  润新知