首页 > Windows开发 > 详细

WPF之数据验证

时间:2020-07-02 15:08:31      阅读:57      评论:0      收藏:0      [点我收藏+]

前台xaml样式:

 <Style TargetType="{x:Type TextBox}">
                <Style.Triggers>
                    <Trigger Property="Validation.HasError" Value="True">
                        <Setter Property="BorderBrush" Value="Red"/>
                        <Setter Property="ToolTip" 
                                Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                    </Trigger>
                </Style.Triggers>
                <Setter Property="Validation.ErrorTemplate">
                    <Setter.Value>
                        <ControlTemplate>
                            <StackPanel>
                                <AdornedElementPlaceholder />
                                <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
                            </StackPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="Width" Value="120" ></Setter>
                <Setter Property="Height" Value="30" ></Setter>
            </Style>

 

 

法一重写ValidationRule:

 public class MarginValidationRule : ValidationRule
    {
        double minMargin;
        double maxMargin;

        public double MinMargin
        {
            get { return this.minMargin; }
            set { this.minMargin = value; }
        }

        public double MaxMargin
        {
            get { return this.maxMargin; }
            set { this.maxMargin = value; }
        }

        public override System.Windows.Controls.ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            double margin;

            // Is a number?
            if (!double.TryParse((string)value, out margin))
            {
                return new System.Windows.Controls.ValidationResult(false, "Not a number.");
            }

            // Is in range?
            if ((margin < this.minMargin) || (margin > this.maxMargin))
            {
                string msg = string.Format("Margin must be between {0} and {1}.", this.minMargin, this.maxMargin);
                return new System.Windows.Controls.ValidationResult(false, msg);
            }

            // Number is valid
            return new System.Windows.Controls.ValidationResult(true, null);
        }
    }

//MVVM viewModel  

public class MarginViewModel : INotifyPropertyChanged { int m_left; public int Left { get { return m_left; } set { m_left = value; OnPropertyChanged("Left"); } } private ICommand _testRelayCommand; public ICommand TestRelayCommand { get { if (_testRelayCommand == null) { _testRelayCommand = new RelayCommand(new Action<object>(OnTestRelayCommandExecuted), new Predicate<object>(OnTestRelayCommandCanExecute)); } return _testRelayCommand; } } public void OnTestRelayCommandExecuted(object para) { MessageBox.Show("OK"); } public bool OnTestRelayCommandCanExecute(object para) { DependencyObject node = para as DependencyObject; return IsValid(node); } // Validate all dependency objects in a window bool IsValid(DependencyObject node) { // Check if dependency object was passed if (node != null) { // Check if dependency object is valid. // NOTE: Validation.GetHasError works for controls that have validation rules attached bool isValid = !Validation.GetHasError(node); if (!isValid) { // If the dependency object is invalid, and it can receive the focus, // set the focus if (node is IInputElement) Keyboard.Focus((IInputElement)node); return false; } } // If this dependency object is valid, check all child dependency objects foreach (object subnode in LogicalTreeHelper.GetChildren(node)) { if (subnode is DependencyObject) { // If a child dependency object is invalid, return false immediately, // otherwise keep checking if (IsValid((DependencyObject)subnode) == false) return false; } } // All dependency objects are valid return true; } public event PropertyChangedEventHandler PropertyChanged; void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }

前台xaml:

  <StackPanel>
            <GroupBox Header="自定义ValidationRules">
                <StackPanel x:Name="stack1" Margin="10">
                    <TextBox Name="leftMarginTextBox" Grid.Column="1" Grid.Row="0">
                        <TextBox.Text>
                            <Binding Path="Left" UpdateSourceTrigger="PropertyChanged">
                                <Binding.ValidationRules>
                                    <local:MarginValidationRule MinMargin="0" MaxMargin="10" />
                                </Binding.ValidationRules>
                            </Binding>
                        </TextBox.Text>
                    </TextBox>
                    <Button Height="20" Width="200" Margin="0,20,0,0" Command="{Binding TestRelayCommand}" CommandParameter="{Binding ElementName=stack1}" >保存</Button>
                </StackPanel>
            </GroupBox>
           
                <Button Height="30" Margin="0,10,0,0"  Width="80" Content="应用" Click="Button_Click"/>
        </StackPanel>

法二 重写实现接口IDataError

   public class Person : NotifyBase, IDataErrorInfo
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    RaisePropertyChanged("Name");
                }
            }
        }

        private int _age;
        public int Age
        {
            get { return _age; }
            set
            {
                if (_age != value)
                {
                    _age = value;
                    RaisePropertyChanged("Age");
                }
            }
        }
        public string Error
        {
            get { return ""; }
        }

        public string this[string columnName]
        {
            get
            {
                //为了避免出现大量的 switch-case,并且将校验逻辑进行分离提高代码复用,于是 DataAnnotations 华丽登场。法3
 //改造下上面的 Person 类,加上 [Range]  ValidationAttribute:(需要添加 System.ComponentModel.DataAnnotations.dll)

                switch (columnName)
                { 
                    case "Age":
                        if (_age < 18)
                        {
                            return "年龄必须在18岁以上。";
                        }
                        break;
                    case "Name":
                        if (_name != null && _name.Length > 10)
                        {
                            return "名字长度最长10";
                        }
                        break;
                }

                return string.Empty;
            }
        }

    }
 public class NotifyBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        internal virtual void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
//ViewModel

  public class PersonViewModel : NotifyBase
    {

        Person m_person = new Person();
        public Person Person
        {
            get { return m_person; }
            set
            {
                m_person = value;
                RaisePropertyChanged("Person");
            }
        }
        private ICommand _testRelayCommand2;
        public ICommand TestRelayCommand2
        {
            get
            {
                if (_testRelayCommand2 == null)
                {
                    _testRelayCommand2 = new RelayCommand(new Action<object>(OnTestRelayCommandExecuted2), new Predicate<object>(OnTestRelayCommandCanExecute2));
                }
                return _testRelayCommand2;
            }
        }

        public void OnTestRelayCommandExecuted2(object para)
        {
            MessageBox.Show("OK");
        }

        public bool OnTestRelayCommandCanExecute2(object para)
        {
            DependencyObject node = para as DependencyObject;
            return IsValid(node);
        }
        // Validate all dependency objects in a window
        bool IsValid(DependencyObject node)
        {
            // Check if dependency object was passed
            if (node != null)
            {
                // Check if dependency object is valid.
                // NOTE: Validation.GetHasError works for controls that have validation rules attached
                bool isValid = !Validation.GetHasError(node);
                if (!isValid)
                {
                    // If the dependency object is invalid, and it can receive the focus,
                    // set the focus
                    if (node is IInputElement) Keyboard.Focus((IInputElement)node);
                    return false;
                }
            }

            // If this dependency object is valid, check all child dependency objects
            foreach (object subnode in LogicalTreeHelper.GetChildren(node))
            {
                if (subnode is DependencyObject)
                {
                    // If a child dependency object is invalid, return false immediately,
                    // otherwise keep checking
                    if (IsValid((DependencyObject)subnode) == false) return false;
                }
            }

            // All dependency objects are valid
            return true;
        }



    }

前台xaml

 <StackPanel Grid.Column="1" Grid.Row="0">
            <GroupBox Header="IDataError">
                <StackPanel x:Name="stack2" Margin="10">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock>Age:</TextBlock>
                        <TextBox Text="{Binding Person.Age, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" Margin="5"/>
                    </StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock>Name:</TextBlock>
                        <TextBox Text="{Binding Person.Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" Margin="5"/>
                    </StackPanel>
                    <Button Height="20" Width="200" Margin="0,20,0,0" Command="{Binding TestRelayCommand2}" CommandParameter="{Binding ElementName=stack2}" >保存</Button>
                </StackPanel>
            </GroupBox>

            <Button Height="30" Margin="0,10,0,0"  Width="80" Content="应用" Click="Button_Click2"/>
        </StackPanel>

 

法三 重写实现接口IDataError优化:

用 DataAnnotions 后,Model 的更加简洁,校验也更加灵活。还可以利用 CustomerValidation 或者
自定义 ValidationAttribute 来进行校验逻辑的进一步分离,错误消息格式化。并且通过反射等技术,完全可以将 IDataErrorInfo 的实现抽成一个抽象类进行封装,编程更加便利。

(1) 自定义 ValidationAttribute
添加了一个针对上面 Person 的 Name 属性是否存在的校验:

  class NameExistsAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            var name = value as string;
            // 这里可以到数据库等存储容器中检索
            if (name != "Felix")
            {
                return false;
            }
            return true;
        }

        public override string FormatErrorMessage(string name)
        {
            return "请输入存在的用户名。";
        }
    }

(2) 利用 CustomerValidationAttribute
先实现一个 public static 的校验方法(必须返回 ValidationResult )

  public class CustomerValidationUtils
    {
        public static System.ComponentModel.DataAnnotations.ValidationResult CheckName(string value)
        {
            if (value == null)
            {
                return new System.ComponentModel.DataAnnotations.ValidationResult("名字不能为空");
            }
            if (value.Length < 8)
            {
                return new System.ComponentModel.DataAnnotations.ValidationResult("名字长度必须大于等于8位。");
            }
            return System.ComponentModel.DataAnnotations.ValidationResult.Success;
        }
  }

下面是应用:

 
//定义了一个实体类的基类,具有通知、实现IDataErrorInfo接口
public class EntityBase : NotifyBase, IDataErrorInfo
    {

        public string Error
        {
            get { return ""; }
        }
        //修改 IDataErrorInfo 的索引器,让它通过 Validator 校验属性
        public string this[string columnName]
        {
            get
            {
                var vc = new ValidationContext(this, null, null);
                vc.MemberName = columnName;
                var res = new List<System.ComponentModel.DataAnnotations.ValidationResult>();
                var result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, res);
                if (res.Count > 0)
                {
                    return string.Join(Environment.NewLine, res.Select(r => r.ErrorMessage).ToArray());
                }
                return string.Empty;
            }
        }

    }
 public class Person3 : EntityBase
    {
        private string _name;

        [MaxLength(10, ErrorMessage = "最长姓名10")]
        [NameExistsAttribute]
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    RaisePropertyChanged("Name");
                }
            }
        }


        private string _nameSeconed;

        [CustomValidation(typeof(CustomerValidationUtils), "CheckName")]
        public string NameSeconed
        {
            get { return _nameSeconed; }
            set { _nameSeconed = value;
              RaisePropertyChanged("NameSeconed");
            }
        }

        private int _age;
        //为了避免出现大量的 switch-case,并且将校验逻辑进行分离提高代码复用,于是 DataAnnotations 华丽登场。
        //改造下上面的 Person 类,加上 [Range]  ValidationAttribute:(需要添加 System.ComponentModel.DataAnnotations.dll)

        [Range(19, 99, ErrorMessage="年龄必须在19与99之间。")]
        public int Age
        {
            get { return _age; }
            set
            {
                if (_age != value)
                {
                    _age = value;
                    RaisePropertyChanged("Age");
                }
            }
        }
      
       
    }

ViewModel:

  public class PersonViewModel3 : NotifyBase
    {

        Person3 m_person = new Person3();
        public Person3 Person
        {
            get { return m_person; }
            set
            {
                m_person = value;
                RaisePropertyChanged("Person");
            }
        }

      


        private ICommand _testRelayCommand3;
        public ICommand TestRelayCommand3
        {
            get
            {
                if (_testRelayCommand3 == null)
                {
                    _testRelayCommand3 = new RelayCommand(new Action<object>(OnTestRelayCommandExecuted3), new Predicate<object>(OnTestRelayCommandCanExecute3));
                }
                return _testRelayCommand3;
            }
        }

        public void OnTestRelayCommandExecuted3(object para)
        {
            MessageBox.Show("OK");
        }

        public bool OnTestRelayCommandCanExecute3(object para)
        {
            DependencyObject node = para as DependencyObject;
            return IsValid(node);
        }
        // Validate all dependency objects in a window
        bool IsValid(DependencyObject node)
        {
            // Check if dependency object was passed
            if (node != null)
            {
                // Check if dependency object is valid.
                // NOTE: Validation.GetHasError works for controls that have validation rules attached
                bool isValid = !Validation.GetHasError(node);
                if (!isValid)
                {
                    // If the dependency object is invalid, and it can receive the focus,
                    // set the focus
                    if (node is IInputElement) Keyboard.Focus((IInputElement)node);
                    return false;
                }
            }

            // If this dependency object is valid, check all child dependency objects
            foreach (object subnode in LogicalTreeHelper.GetChildren(node))
            {
                if (subnode is DependencyObject)
                {
                    // If a child dependency object is invalid, return false immediately,
                    // otherwise keep checking
                    if (IsValid((DependencyObject)subnode) == false) return false;
                }
            }

            // All dependency objects are valid
            return true;
        }



    }

 

源代码下载

WPF之数据验证

原文:https://www.cnblogs.com/ZHXI/p/13224137.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!