INotifyDataErrorInfo实现属性验证

同步模式

引用网址:http://mark.mymonster.nl/2011/02/22/validating-our-viewmodel

public abstract class ViewModelBase : GalaSoft.MvvmLight.ViewModelBase, INotifyDataErrorInfo
{
    private readonly IDictionary<string, IList<string>> _errors = new Dictionary<string, IList<string>>();
 
    protected ViewModelBase()
    {
    }
 
    protected ViewModelBase(IMessenger messenger)
        : base(messenger)
    {
    }
 
    #region INotifyDataErrorInfo Members
 
    public IEnumerable GetErrors(string propertyName)
    {
        if (_errors.ContainsKey(propertyName))
        {
            IList<string> propertyErrors = _errors[propertyName];
            foreach (string propertyError in propertyErrors)
            {
                yield return propertyError;
            }
        }
        yield break;
    }
 
    public bool HasErrors
    {
        get { return _errors.Count > 0; }
    }
 
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
 
    #endregion
 
    protected void NotifyErrorsChanged(string propertyName)
    {
        if (ErrorsChanged != null)
            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
    }
 
    protected void ValidateProperty(string propertyName, object value)
    {
        ViewModelBase objectToValidate = this;
        var results = new List<ValidationResult>();
        bool isValid = Validator.TryValidateProperty(
            value,
            new ValidationContext(objectToValidate, null, null)
                {
                    MemberName = propertyName
                },
            results);
 
        if (isValid)
            RemoveErrorsForProperty(propertyName);
        else
            AddErrorsForProperty(propertyName, results);
 
        NotifyErrorsChanged(propertyName);
    }
 
    private void AddErrorsForProperty(string propertyName, IEnumerable<ValidationResult> validationResults)
    {
        RemoveErrorsForProperty(propertyName);
        _errors.Add(propertyName, validationResults.Select(vr => vr.ErrorMessage).ToList());
    }
 
    private void RemoveErrorsForProperty(string propertyName)
    {
        if (_errors.ContainsKey(propertyName))
            _errors.Remove(propertyName);
    }
 
    public bool ValidateObject()
    {
        ViewModelBase objectToValidate = this;
        _errors.Clear();
        Type objectType = objectToValidate.GetType();
        PropertyInfo[] properties = objectType.GetProperties();
        foreach (PropertyInfo property in properties)
        {
            if (property.GetCustomAttributes(typeof (ValidationAttribute), true).Any())
            {
                object value = property.GetValue(objectToValidate, null);
                ValidateProperty(property.Name, value);
            }
        }
 
        return !HasErrors;
    }
}

异步模式

引用网址: http://burnaftercoding.com/post/asynchronous-validation-with-wpf-4-5/

public class ValidatableModel : INotifyDataErrorInfo, INotifyPropertyChanged
{
    private ConcurrentDictionary<string, List<string>> _errors = 
        new ConcurrentDictionary<string, List<string>>();

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
        ValidateAsync();
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public void OnErrorsChanged(string propertyName)
    {
        var handler = ErrorsChanged;
        if (handler != null)
            handler(this, new DataErrorsChangedEventArgs(propertyName));
    }

    public IEnumerable GetErrors(string propertyName)
    {
        List<string> errorsForName;
        _errors.TryGetValue(propertyName, out errorsForName);
        return errorsForName;
    }

    public bool HasErrors
    {
        get { return _errors.Any(kv => kv.Value != null && kv.Value.Count > 0); }
    }

    public Task ValidateAsync()
    {
        return Task.Run(() => Validate());
    }

    private object _lock = new object();
    public void Validate()
    {
        lock (_lock)
        {
            var validationContext = new ValidationContext(this, null, null);
            var validationResults = new List<ValidationResult>();
            Validator.TryValidateObject(this, validationContext, validationResults, true);

            foreach (var kv in _errors.ToList())
            {
                if (validationResults.All(r => r.MemberNames.All(m => m != kv.Key)))
                {
                    List<string> outLi;
                    _errors.TryRemove(kv.Key, out outLi);
                    OnErrorsChanged(kv.Key);
                }
            }

            var q = from r in validationResults
                    from m in r.MemberNames
                    group r by m into g
                    select g;

            foreach (var prop in q)
            {
                var messages = prop.Select(r => r.ErrorMessage).ToList();

                if (_errors.ContainsKey(prop.Key))
                {
                    List<string> outLi;
                    _errors.TryRemove(prop.Key, out outLi);
                }
                _errors.TryAdd(prop.Key, messages);
                OnErrorsChanged(prop.Key);
            }
        }
    }
}

示例

public class UserInput : ValidatableModel
{
    private string _userName;
    private string _email;
    private string _repeatEmail;

    [Required]
    [StringLength(20)]
    public string UserName
    {
        get { return _userName; }
        set { _userName = value; RaisePropertyChanged("UserName"); }
    }

    [Required]
    [EmailAddress]
    [StringLength(60)]
    public string Email
    {
        get { return _email; }
        set { _email = value; RaisePropertyChanged("Email"); }
    }

    [Required]
    [EmailAddress]
    [StringLength(60)]
    [CustomValidation(typeof(UserInput), "SameEmailValidate")]
    public string RepeatEmail
    {
        get { return _repeatEmail; }
        set { _repeatEmail = value; RaisePropertyChanged("RepeatEmail"); }
    }

    public static ValidationResult SameEmailValidate(object obj, ValidationContext context)
    {
        var user = (UserInput)context.ObjectInstance;
        if (user.Email != user.RepeatEmail)
        {
            return new ValidationResult("The emails are not equal", 
                new List<string> { "Email", "RepeatEmail" });
        }
        return ValidationResult.Success;
    }
}
<TextBox Text="{Binding UserName, ValidatesOnNotifyDataErrors=True}"/>
<TextBox Text="{Binding Password, ValidatesOnNotifyDataErrors=True}" />
<TextBox Text="{Binding RepeatPassword, ValidatesOnNotifyDataErrors=True}" />

示例验证图片

异步完整代码

github地址: https://github.com/anthyme/AsyncValidation

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在WPF中,可以通过以下几种方法对TextBox进行验证: 1. 使用ValidationRule类:可以自定义一个ValidationRule类,然后将其绑定到TextBox的ValidationRules属性上,以实现对TextBox输入内容进行验证。例如: ```xaml <TextBox> <TextBox.Text> <Binding Path="UserName" UpdateSourceTrigger="PropertyChanged"> <Binding.ValidationRules> <local:UserNameValidationRule /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> ``` 其中,local:UserNameValidationRule是自定义的ValidationRule类。 2. 使用IDataErrorInfo接口:可以在ViewModel中实现IDataErrorInfo接口,然后将TextBox的Binding.Validation属性设置为True,以实现对TextBox输入内容进行验证。例如: ```xaml <TextBox Text="{Binding UserName, ValidatesOnDataErrors=True}" /> ``` 其中,ViewModel中实现IDataErrorInfo接口的代码如下: ```csharp public class UserViewModel : IDataErrorInfo { public string UserName { get; set; } public string Error => null; public string this[string propertyName] { get { string error = null; switch (propertyName) { case "UserName": if (string.IsNullOrEmpty(UserName)) error = "请输入用户名"; break; } return error; } } } ``` 3. 使用INotifyDataErrorInfo接口:与IDataErrorInfo类似,可以在ViewModel中实现INotifyDataErrorInfo接口,然后将TextBox的Binding.Validation属性设置为True,以实现对TextBox输入内容进行验证。不同的是,INotifyDataErrorInfo可以实现异步验证。例如: ```xaml <TextBox Text="{Binding UserName, ValidatesOnNotifyDataErrors=True}" /> ``` 其中,ViewModel中实现INotifyDataErrorInfo接口的代码如下: ```csharp public class UserViewModel : INotifyDataErrorInfo { private string _userName; public string UserName { get { return _userName; } set { if (_userName != value) { _userName = value; ValidateUserName(); OnPropertyChanged("UserName"); } } } private readonly Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>(); public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; public bool HasErrors => _errors.Any(); public IEnumerable GetErrors(string propertyName) { if (string.IsNullOrEmpty(propertyName)) return _errors.Values; return _errors.ContainsKey(propertyName) ? _errors[propertyName] : null; } private void ValidateUserName() { if (string.IsNullOrEmpty(UserName)) { AddError("UserName", "请输入用户名"); } else { RemoveError("UserName", "请输入用户名"); } } private void AddError(string propertyName, string error) { if (!_errors.ContainsKey(propertyName)) _errors[propertyName] = new List<string>(); if (!_errors[propertyName].Contains(error)) { _errors[propertyName].Add(error); ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); } } private void RemoveError(string propertyName, string error) { if (_errors.ContainsKey(propertyName) && _errors[propertyName].Contains(error)) { _errors[propertyName].Remove(error); if (_errors[propertyName].Count == 0) _errors.Remove(propertyName); ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); } } protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; } ``` 以上就是对WPF中TextBox进行验证的三种方法的详细描述。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值