5

我有一个模型:

public class Product
{
    public int Rating { get; set; }
    ...
}

和一个视图模型:

public class ProductViewModel: IDataErrorProvider
{
    public int Temperature { get; set; }
    public Product CurrentProduct { get; set; }

    public string this[string columnName]
    {
        get
        {
            if (columnName == "Rating")
            {
                if (CurrentProduct.Rating > Temperature)
                    return "Rating is too high for current temperature";
            }
            return null;
        }
    }
}

我的视图有一个 ProductViewModel 实例作为 DataContext。该视图具有以下字段:

<TextBox Text={Binding Path=CurrentProduct.Rating, ValidatesOnDataErrors=True} .../>

默认情况下,验证发生在绑定对象 (Product) 的 IDataErrorProvider 上,而不是 DataContext (ProductViewModel) 上。所以在上面的例子中,ProductViewModel 验证永远不会被调用。这只是一个简单的例子,但说明了问题。该模型不(也不应该)知道温度,因此设计规定 VM 应该在该字段上执行验证。

是的,我可以破解它并直接在 ViewModel 中复制模型的绑定属性,但我原以为必须有一种更简单的方法将调用重定向到 VM 而不是模型?

4

2 回答 2

2

如果您希望您的视图模型通过 IDataErrorInfo 验证名为“Rating”的属性,那么您的视图模型实际上必须有一个名为 Rating 的属性并且您必须绑定到它,这意味着在视图模型中复制模型的绑定属性。

无论如何,这篇博客文章对您来说可能很有趣(Validating Business Rules in MVVM)。作者在viewmodel可以设置的模型中添加了一个Validation delegate。这使您可以使用未知的数据来验证模型,例如示例中的温度。

于 2012-10-09T07:49:19.260 回答
1

我以前遇到过这个问题,我的解决方案是从我的验证委托中公开一个验证委托,Models在验证类时检查它,并且可以使用它来将附加验证挂钩到与自身ViewModel无关的类Model

例如,我会使用看起来像这样的代码ViewModel将验证委托附加到Model它的集合

public class ProductViewModel
{
    public int Temperature { get; set; }

    private product _currentProduct;
    public Product CurrentProduct 
    { 
        get { return _currentProduct; }
        set
        {
            if (value != _currentProduct)
            {
                if (_currentProduct != null)
                    _currentProduct.RemoveValidationDelegate(ValidateProduct);

                _currentProduct = value;

                if (_currentProduct != null)
                    _currentProduct.AddValidationDelegate(ValidateProduct);

                RaisePropertyChanged("CurrentProduct");
            }
        }
    }

    // Product Validation Delegate to verify temperature
    private string ValidateProduct(object sender, string propertyName)
    {
        if (propertyName == "Rating")
        {
            if (CurrentProduct.Rating > Temperature)
                return "Rating is too high for current temperature";
        }

        return null;
    }
}

ValidationDelegate添加到的实际代码Model非常通用,因此我通常将它放在 a 中BaseViewModel,因此所有模型都可以拥有此功能,而无需我为每个模型输入

#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-10-10T19:57:17.380 回答