Объединение DataAnnotations и IDataErrorInfo для WPF - PullRequest
5 голосов
/ 16 августа 2011

Я пишу приложение 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 в качестве параметра), но я использую обычные свойства CLR с OnPropertyChanged("MyProperty"), вызываемым на установщике.

Есть ли хороший способ подключить проверку к IDataErrorInfo интерфейс?

Ответы [ 2 ]

5 голосов
/ 18 августа 2011

Используя приведенный выше код в качестве отправной точки, я получил эту работу через 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 голос
/ 22 марта 2015

Я знаю, что этот пост старый, но я недавно решил эту проблему с помощью этого поста, хотя и вносил некоторые улучшения. Я хотел бы поделиться с моей реализацией ViewModelBase IDataErrorInfo. Он использует скомпилированные выражения для методов получения свойств, что ускоряет доступ к значению свойства. Я также запускаю компиляции выражений в фоновом потоке, когда тип загружается в память. Надеемся, что он завершает компиляцию перед первым вызовом 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;
    }
}
...