我正在编写WPF应用程序,我想使用数据注释来指定Required
字段,Range
等内容。
我的ViewModel类使用常规的INotifyPropertyChanged
接口,我可以使用C#4 Validator
轻松验证整个对象,但如果它们没有正确验证,我还希望这些字段突出显示为红色。我在这里找到了这篇博文(http://blogs.microsoft.co.il/blogs/tomershamam/archive/2010/10/28/wpf-data-validation-using-net-data-annotations-part-ii.aspx )讨论如何编写基本视图模型来实现IDataErrorInfo
并简单地使用Validator,但实现实际上并没有编译,也无法看到它是如何工作的。有问题的方法是:
/// <summary>
/// Validates current instance properties using Data Annotations.
/// </summary>
/// <param name="propertyName">This instance property to validate.</param>
/// <returns>Relevant error string on validation failure or <see cref="System.String.Empty"/> on validation success.</returns>
protected virtual string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
string error = string.Empty;
var value = GetValue(propertyName);
var results = new List<ValidationResult>(1);
var result = Validator.TryValidateProperty(
value,
new ValidationContext(this, null, null)
{
MemberName = propertyName
},
results);
if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;
}
return error;
}
问题是GetValue
未提供。他可能正在讨论继承GetValue
时出现的DependencyObject
,但语法仍无效(它希望您将DependencyProperty
作为参数传递)但我正在使用在setter上调用OnPropertyChanged("MyProperty")
的常规CLR属性。
有没有一种方法可以将验证连接到IDataErrorInfo
界面?
答案 0 :(得分:5)
使用上面的代码作为起点,我通过IDataErrorInfo完成了这项工作。
当您只拥有属性名称时,您的问题集中在获取属性的值,反射可以在这里提供帮助。
public string this[string property]
{
get
{
PropertyInfo propertyInfo = this.GetType().GetProperty(property);
var results = new List<ValidationResult>();
var result = Validator.TryValidateProperty(
propertyInfo.GetValue(this, null),
new ValidationContext(this, null, null)
{
MemberName = property
},
results);
if (!result)
{
var validationResult = results.First();
return validationResult.ErrorMessage;
}
return string.Empty;
}
}
答案 1 :(得分:1)
我知道这篇文章已经过时了,但我最近在这篇文章的帮助下解决了这个问题,同时还在进行一些优化。我想分享我的ViewModelBase的IDataErrorInfo实现。它使用属性getter的编译表达式来加速属性值访问。当类型加载到内存中时,我还会在后台线程上触发表达式编译。希望它在第一次调用OnValidate之前完成编译,因为表达式编译可能有点慢。谢谢和欢呼。
public abstract class ViewModelBase<TViewModel> : IDataErrorInfo
where TViewModel : ViewModelBase<TViewModel>
{
string IDataErrorInfo.Error
{
get { throw new NotSupportedException("IDataErrorInfo.Error is not supported, use IDataErrorInfo.this[propertyName] instead."); }
}
string IDataErrorInfo.this[string propertyName]
{
get { return OnValidate(propertyName, propertyGetters.Result[propertyName]((TViewModel)this)); }
}
private static Task<Dictionary<string, Func<TViewModel, object>>> propertyGetters = Task.Run(() =>
{
return typeof(TViewModel).GetProperties()
.Select(propertyInfo =>
{
var viewModel = Expression.Parameter(typeof(TViewModel));
var property = Expression.Property(viewModel, propertyInfo);
var castToObject = Expression.Convert(property, typeof(object));
var lambda = Expression.Lambda(castToObject, viewModel);
return new
{
Key = propertyInfo.Name,
Value = (Func<TViewModel, object>)lambda.Compile()
};
})
.ToDictionary(pair => pair.Key, pair => pair.Value);
});
protected virtual string OnValidate(string propertyName, object propertyValue)
{
var validationResults = new List<ValidationResult>();
var validationContext = new ValidationContext(this, null, null) { MemberName = propertyName };
if (!Validator.TryValidateProperty(propertyValue, validationContext, validationResults))
{
return validationResults.First().ErrorMessage;
}
return string.Empty;
}
}