是否存在使用 MVVM 与 Silverlight 中的 RIA 服务结合构建和验证数据的最佳实践或广泛接受的方法?

这是我的问题的症结所在。假设我有一个 EmployeeView、EmployeeViewModel 和一些 Employee 实体。在常规 RIA 应用程序中,我将在视图上公开 Employee 实体,并且“免费”获得验证,因为实体实现了 INotifyDataErrorInfo 和 IDataErrorInfo(正确吗?)。

现在,如果我想通过 ViewModel 而不是直接通过实体公开一些 Employee 属性,那么它会变得更加复杂。我可以直接公开我需要的位并将它们挂接到后端的实体中,如下所示:

    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 框架,但我很想看到普遍适用的答案。

有帮助吗?

解决方案

我使用与Caliburn.Micro RIA和我与我的客户端验证的解决方案非常满意。

我所做的是把ValidationBaseViewModel(由Caliburn.Micro提供)和我的实际应用程序的虚拟机(在你的情况Screen)之间的EmployeeViewModelValidationBaseViewModel工具INotifyDataErrorInfo所以你谈论的管道代码只能写一次。

:我然后从(Caliburn.Micro)ValidationBaseViewModel的用下面的代码的重写添加/删除/错误的通知经由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);
}

这实际上是在另一VM(ValidationBaseViewModel和EmployeeViewModel之间)具有以下定义:

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

其中Entity是RIA的System.ServiceModel.DomainServices.Client.Entity_editing类部件是由当前的VM编辑的这种类型TEdit的一个实例。

在与卡利组合协同程序这允许我做一些凉的东西等的情况如下:

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

    ...
}

其他提示

如果您不想使用外部资源或框架,那么我可以有一个 ViewModelBase 实施 INotifyDataErrorInfo.

那个班级会有 ValidateProperty(string propertyName, object value) 验证特定属性,以及 Validate() 方法来验证整个对象。内部使用 Validator 类返回 ValidationResults。
如果使用反光板的话,可以 挺容易 通过模仿验证过程来实现 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));
  }
}

可以使用一个局部类来扩展你的实体和添加数据验证那里通过IDataErrorInfo的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top