我有一个模型:
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 而不是模型的调用?
如果希望视图模型验证 IDataErrorInformation 的名为"Rating"的属性,则视图模型实际上必须具有一个名为 Rating 的属性,并且必须绑定到该属性,这意味着在视图模型中复制模型的绑定属性。
无论如何,这篇博客文章可能会让您感兴趣(在 MVVM 中验证业务规则)。作者将验证委托添加到视图模型可以设置的模型中。这允许您使用未知的数据(如示例中的温度)验证模型。
我以前遇到过这个问题,我的解决方案是从我的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
的实际代码非常通用,因此我通常将其放在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
如果您想查看另一个示例,我也在我的博客文章中概述了这种方法。