Проверка данных в пользовательском интерфейсе с помощью WCF RIA и шаблона MVVM - PullRequest
2 голосов
/ 17 ноября 2010

Существует ли передовая практика или общепринятый способ структурирования и проверки данных с использованием MVVM в сочетании со службами RIA в Silverlight?

Вот суть моей проблемы.Допустим, у меня есть EmployeeView, EmployeeViewModel и некоторая сущность Employee.В обычных приложениях RIA я выставлю эту сущность Employee в представлении и получу проверку «бесплатно», потому что сущности реализуют INotifyDataErrorInfo и IDataErrorInfo (правильно?).

Теперь, если я хочу выставить некоторые свойства Employee черезViewModel, а не напрямую через Entity, тогда это становится более сложным.Я мог бы выставить нужные мне биты и подключить их к сущности на бэкенде, например:

    private Employee _employee;

    public EmployeeViewModel()
    {
        _employee = new Employee();
    }

    public string Name
    {
        get { return _employee.Name; }
        set
        {
            _employee.Name = value;
            // fire property change, etc.
        }
    }

... но я потерял вкусную «бесплатную» проверку сущностей.В противном случае я мог бы выставить сущность непосредственно в модели представления, например, так:

    private Employee _employee;
    public Employee Employee
    {
        get { return _employee; }
    }

    public EmployeeViewModel()
    {
        _employee = new Employee();
    }

В этом случае представление будет привязываться непосредственно к сущности Employee и находить там его свойства, например:

<StackPanel DataContext="{Binding Employee}">
    <TextBox Text="{Binding Name}" />
</StackPanel>

Используя этот метод, мы получаем «бесплатную» проверку, но это не совсем чистая реализация MVVM.

Третий вариант - реализовать INotifyDataErrorInfo и IDataErrorInfo самостоятельно в виртуальных машинах, но это кажетсякак ужасно много сланцевого кода, учитывая, как легко было бы мне использовать вышеупомянутое решение и получить что-то чуть менее «чистое», но чертовски намного проще в конце дня.

ТакНаверное, мой вопрос: какой из этих подходов подходит в какой ситуации?Есть ли лучший подход, который мне не хватает?

В случае, если это уместно, я смотрю на структуру Caliburn.Micro MVVM, но мне было бы интересно увидеть ответы, которые применимы в общем.

Ответы [ 3 ]

2 голосов
/ 11 апреля 2011

Я использую RIA с Caliburn.Micro и очень доволен своим решением для проверки на стороне клиента.

Что я сделал, так это положил ValidationBaseViewModel между Screen (предоставлено Caliburn.Micro)и мои действительные прикладные виртуальные машины (EmployeeViewModel в вашем случае).ValidationBaseViewModel реализует INotifyDataErrorInfo, так что сантехнический код, о котором вы говорите, пишется только один раз.Затем я добавляю / удаляю / уведомляю об ошибках через ValidationBaseViewModel из переопределения (Caliburn.Micro) PropertyChangedBase.NotifyOfPropertyChange со следующим кодом:

public override void NotifyOfPropertyChange(string property)
{
    if (_editing == null)
        return;

    if (HasErrors)
        RemoveErrorFromPropertyAndNotifyErrorChanges(property, 100);

    if (_editing.HasValidationErrors)
    {
        foreach (var validationError in
                       _editing.ValidationErrors
                               .Where(error => error.MemberNames.Contains(property)))
        {
            AddErrorToPropertyAndNotifyErrorChanges(property, new ValidationErrorInfo() { ErrorCode = 100, ErrorMessage = validationError.ErrorMessage });
        }
    }

    base.NotifyOfPropertyChange(property);
}

Это на самом деле в другой виртуальной машине (между ValidationBaseViewModel иEmployeeViewModel) со следующим определением:

public abstract class BaseEditViewModel<TEdit> :
                                ValidationBaseViewModel where TEdit : Entity

, где Entity - это RIA System.ServiceModel.DomainServices.Client.Entity, а член класса _editing - это экземпляр этого типа TEdit, который редактируется текущей виртуальной машиной.

В сочетании с сопрограммами Caliburn это позволяет мне делать некоторые интересные вещи, такие как:

[Rescue]
public IEnumerable<IResult> Save()
{
    if (HasErrors)
    {
        yield return new GiveFocusByName(PropertyInError);
        yield break;
    }

    ...
}
0 голосов
/ 13 мая 2011

Если вы не хотите использовать внешние ресурсы или фреймворки, тогда у вас может быть ViewModelBase, который реализует INotifyDataErrorInfo.

Этот класс будет иметь ValidateProperty(string propertyName, object value) для проверки конкретного свойства и Validate() метод для проверки всего объекта. Внутренне используйте класс Validator для возврата ValidationResult s.
Если вы используете отражатель, довольно просто достичь, подражая процессу проверки в самом классе Entity, в ViewModelBase.

Хотя это не «бесплатно», это все же относительно дешево.

Вот пример реализации IDataErrorInfo. Хотя это и не проверено, вы получите идею.

public class ViewModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
{

  /*
   * InotifyPropertyChanged implementation
   * Consider using Linq expressions instead of string names
   */

  public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
  public IEnumerable GetErrors(string propertyName)
  {
    if (implValidationErrors == null) return null;
    return ImplValidationErros.Where(ve =>
      ve.MemberNames.Any(mn => mn == propertyName));
  }

  public bool HasErrors
  {
    get
    {
      return implValidationErrors == null || ImplValidationErros.Any();
    }
  }

  private List<ValidationResult> implValidationErrors;
  private List<ValidationResult> ImplValidationErros
  {
    get
    {
      return implValidationErrors ?? 
        (implValidationErrors = new List<ValidationResult>());
    }
  }
  private ReadOnlyCollection<ValidationResult> validationErrors;
  [Display(AutoGenerateField = false)]
  protected ICollection<ValidationResult> ValidationErrors
  {
    get
    {
      return validationErrors ?? 
        (validationErrors =
        new ReadOnlyCollection<ValidationResult>(ImplValidationErros));
    }
  }
  protected void ValidateProperty(string propertyName, object value)
  {
    ValidationContext validationContext =
      new ValidationContext(this, null, null);
    validationContext.MemberName = propertyName;
    List<ValidationResult> validationResults =
      new List<ValidationResult>();

    Validator.TryValidateProperty(
      value, 
      validationContext, 
      validationResults);

    if (!validationResults.Any()) return;

    validationResults
      .AddRange(ValidationErrors
      .Where(ve =>
        !ve.MemberNames.All(mn =>
          mn == propertyName)));

    implValidationErrors = validationResults;

    if (ErrorsChanged != null)
      ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
  }
}
0 голосов
/ 18 ноября 2010

вы можете использовать частичный класс, чтобы расширить свои права и добавить туда проверку данных через idataerrorinfo.

...