5

我正在编写一个 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和简单地使用验证器,但实现实际上并没有编译,我也看不到它是如何工作的。有问题的方法是这样的:

    /// <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作为参数传递)但我正在使用常规 CLR 属性并OnPropertyChanged("MyProperty")在 setter 上调用。

有没有很好的方法将验证连接到IDataErrorInfo接口?

4

2 回答 2

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;
   }
}
于 2011-08-18T00:03:22.760 回答
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;
    }
}
于 2015-03-21T21:17:59.740 回答