同步模式
引用网址: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}" />