5

我正在使用 MVVM,我想使用 IDataErrorInfo 来验证我的视图。

我当前的实现包括嵌套对象和不同的视图模型。例如,业务实体“客户”包含业务实体“地址”。我在我的视图中直接访问地址,例如“Customer.Address”。要验证地址中的更改,我必须在地址中实现 IDataErrorInfo。

我在不同的视图/视图模型中使用客户或地址。在不同的视图/视图模型中使用会导致不同的验证行为。因此,在实体本身中实现验证是不够的。

在 ViewModel 中公开我想直接更改的属性(创建直接设置/获取实体的新属性)似乎使 ViewModel 过于僵化。而且太大了。

我不能从基类继承,因为某些业务实体已经从其他对象派生(这是我无法改变的事实)。我目前看到的唯一选择是将 ViewModel 的接口添加到业务实体,并将业务实体中的 this[] 调用转发到该 ViewModel 接口。

是否有关于如何在 ViewModel 中验证这些嵌套对象的最佳实践?

编辑:验证我不认为业务对象中的验证是一个可用的想法的另一个原因是,我需要在我的 ViewModel 中使用不同的业务对象来验证视图和数据条目。

4

3 回答 3

7

我过去这样做的一种方法是在 Model 上公开一个 ValidationDelegate,它允许 ViewModel 将自己的验证代码附加到模型。

通常我这样做是因为我将Model层用作普通数据对象,所以我的模型只验证基本的东西,例如最大长度或非空值,而任何不特定于数据模型的高级验证都在 ViewModel 中完成。这通常包括诸如确保项目是唯一的,或者用户有权将值设置为特定范围的事情,甚至像您的情况一样,验证仅针对特定操作存在。

public class CustomerViewModel
{
    // Keeping these generic to reduce code here, but it
    // should include PropertyChange notification
    public AddressModel Address { get; set; }

    public CustomerViewModel()
    {
        Address = new AddressModel();
        Address.AddValidationDelegate(ValidateAddress);
    }

    // Validation Delegate to validate Adderess
    private string ValidateAddress(object sender, string propertyName)
    {
        // Do your ViewModel-specific validation here.
        // sender is your AddressModel and propertyName 
        // is the property on the address getting validated

        // For example:
        if (propertyName == "Street1" && string.IsNullOrEmpty(Address.Street1))
            return "Street1 cannot be empty";

        return null;
    }
}

这是我通常用于验证委托的代码:

#region IDataErrorInfo & Validation Members

#region Validation Delegate

public delegate string ValidationDelegate(
    object sender, string propertyName);

private List<ValidationDelegate> _validationDelegates = 
    new List<ValidationDelegate>();

public void AddValidationDelegate(ValidationDelegate func)
{
    _validationDelegates.Add(func);
}

public void RemoveValidationDelegate(ValidationDelegate func)
{
    if (_validationDelegates.Contains(func))
        _validationDelegates.Remove(func);
}

#endregion // Validation Delegate

#region IDataErrorInfo for binding errors

string IDataErrorInfo.Error { get { return null; } }

string IDataErrorInfo.this[string propertyName]
{
    get { return this.GetValidationError(propertyName); }
}

public string GetValidationError(string propertyName)
{
    string s = null;

    foreach (var func in _validationDelegates)
    {
        s = func(this, propertyName);
        if (s != null)
            return s;
    }

    return s;
}

#endregion // IDataErrorInfo for binding errors

#endregion // IDataErrorInfo & Validation Members
于 2012-09-17T13:56:46.480 回答
1

在不同的视图/视图模型中使用会导致不同的验证行为。

因此,您有不同的视图模型。如果您不能从某些基本视图模型继承这些视图模型,请使用聚合:

public class Address {}

public class AddressViewModel1 : IDataErrorInfo
{
  private readonly Address address;
  // other stuff here
}

public class AddressViewModel2 : IDataErrorInfo
{
  private readonly Address address;
  // other stuff here
}
于 2012-09-17T11:40:59.323 回答
0

对于每个不同的视图模型,使用依赖注入并向客户对象注入验证服务怎么样?

但我认为在你的视图模型中实现 idataerrorinfo 和所有需要的属性会更干净,但当然要多工作一次。

于 2012-09-17T13:50:30.483 回答