我想尽可能多地为这个问题提供背景信息,但总的来说,我基本上是在问两个问题:
- 当setter不抛出异常时,WPF是否总是在设置绑定属性后调用getter?
- 在 ViewModel 实现时 setter 发生错误后,是否可以防止调用绑定属性的 getter
IDataErrorInfo
?
我目前有一个模型类,它通过从属性设置器抛出异常来实现验证。此外,许多属性是耦合的,因此修改其中一个的值可能会导致重新计算其他几个。它实现INotifyPropertyChanged
在发生重新计算时向外部侦听器发出警报。它看起来像这样:
public class Model : INotifyPropertyChanged
{
private double property1;
public double Property1
{
get { return property1; }
set
{
if (value < 0.0)
throw new Exception("Property1 cannot be less than 0.0.");
property1 = value;
OnPropertyChanged(new PropertyChangedEventArgs("Property1"));
}
}
// ...Everything needed to support INotifyPropertyChanged...
}
最初,我为这个类实现了 ViewModel 以充当模型属性的薄包装器,在发生错误时提供额外的视图特定行为(标记无效数据、禁用按钮等):
public class ViewModel : INotifyPropertyChanged
{
private readonly Model model;
public ViewModel()
{
model = new Model();
model.PropertyChanged += (sender, args) => OnPropertyChanged(args);
}
public string Property1
{
get { return model.Property1.ToString(); }
set
{
try
{
model.Property1 = Double.Parse(value);
}
catch (Exception)
{
// Perform any view-specific actions
throw;
}
}
}
// ...Everything needed to support INotifyPropertyChanged
}
值得注意的是,ViewModel 没有任何额外的支持字段。它的所有属性都直接链接到 Model 中的相应属性,并且PropertyChanged
来自 Model 的任何通知都由 ViewModel 传递。
但是,我经常听说使用异常进行验证可能会受到限制,并且我开始意识到这一点,特别是在此应用程序中对交叉耦合验证规则的需求有所增加。
我不想改变模型的行为,因为它已经在其他几个地方使用,但我着手改变 ViewModel 来实现IDataErrorInfo
:
public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private readonly Model model;
public ViewModel()
{
model = new Model();
model.PropertyChanged += (sender, args) =>
{
errorList.Remove(args.PropertyName);
OnPropertyChanged(args);
};
}
public string Property1
{
get { return model.Property1.ToString(); }
set
{
try
{
model.Property1 = Double.Parse(value);
}
catch(Exception ex)
{
// Perform any view-specific actions
errorList["Property1"] = ex.Message;
}
}
}
private readonly Dictionary<string, string> errorList = new Dictionary<string, string>();
public string this[string propertyName]
{
get
{
string errorMessage;
if (errorList.TryGetValue(propertyName, out errorMessage))
return errorMessage;
return String.Empty;
}
}
public string Error { get { return String.Empty; } }
// ...Everything needed to support INotifyPropertyChanged
}
但是,这会导致行为发生不必要的剧烈变化。使用异常时,用户输入无效值后,Validation.ErrorTemplate
控件会显示,无效值保留在控件中,从而给用户更正错误的机会。使用时IDataErrorInfo
,WPF 似乎在 setter 完成后调用属性 getter。由于每当发生错误时模型都没有更改,因此将无效值替换为以前的值。所以现在我有一个控件显示Validation.ErrorTemplate
但显示的值是 VALID !
PropertyChanged
在我看来,WPF 会在没有收到通知的情况下自动调用属性获取器(在窗口初始化之后),这似乎很疯狂。并且它不会在抛出异常后尝试调用getter,那么为什么在IDataErrorInfo
使用时会这样做呢?
我做错了什么导致这种行为吗?有没有办法阻止 WPF 在 ViewModel 实现时在 setter 中发生错误后调用 ViewModel 中的属性 getter IDataErrorInfo
?
我尝试将支持字段添加到 ViewModel 以存储无效值,但由于可以在 ViewModel 之外修改模型属性(这就是它INotifyPropertyChanged
首先实现的原因),解决方案最终变得非常复杂并且在具有不同技能水平的程序员的环境中基本上是不可持续的。