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

猜你喜欢

转载自blog.csdn.net/sdhongjun/article/details/83479171