我有一个基于MVVM的窗口,里面有很多控件,我的模型实现了IDataErrorInfo
。
还有一个SaveCommand
按钮,通过分析Model.Error
属性来执行验证。
视图显示控件周围的默认红色边框,错误仅当我更改特定控件的值时,或者当我使用PropertyChanged通知该属性的更改时。
即使我没有触摸控件,如何强制View显示所有验证错误?
我的所有验证绑定都包含ValidatesOnDataErrors=True, NotifyOnValidationError=True
。
我知道一个解决方案是有一个包含所有错误的聚合框,但我更愿意在每个控件的基础上显示错误。
我不想为ViewModel中的每个绑定属性触发Model.NotifyPropertyChanged
。
我使用的是WPF 4.0,而不是Silverlight,因此INotifyDataErrorInfo
无效。
答案 0 :(得分:13)
你提到你不想为你绑定的属性提高属性,但这实际上是最简单的方法。对于viewmodel中的所有属性,将调用不带参数的PropertyChanged。
或者,您可以在任何控件上更新绑定(并强制重新验证),如下所示:
myControl.GetBindingExpression(ControlType.ControlProperty).UpdateSource();
答案 1 :(得分:2)
到目前为止,我发现的最佳解决方案是将DataContext更改为null并返回ViewModel的实例。
这会触发DataContext
绑定到InnerViewModel
的视图的控件更新:
public void ForceUpdateErrors() {
var tmpInnerVM = _mainViewModel.InnerViewModel;
_mainViewModel.InnerViewModel = null;
_mainViewModel.InnerViewModel = tmpInnerVM;
}
建议在此技巧后检查是否没有数据丢失。我有一个案例,这个代码触发ComboBox.SelectedItem的源更新与null但我设法解决它。这是因为使用基于资源的BindingProxy和跨越控制层次结构的DataContext=null
传播顺序。
答案 2 :(得分:1)
这个'Hack'暂时为我工作,强制InotifyChanged事件,只需将该控件分配给它自己的内容。在评估绑定的HasError函数之前执行此操作。例如,文本框将是:
((TextBox)child).Text = ((TextBox)child).Text;
然后是一个完整的例子(在我听到这不是真正的MVVM之前,我直接在网格上获得了一个句柄,以便于显示此代码snipet)
public bool Validate()
{
bool hasErr = false;
for (int i = 0; i != VisualTreeHelper.GetChildrenCount(grd); ++i)
{
DependencyObject child = VisualTreeHelper.GetChild(grd, i);
if (child is TextBox)
{
bool pp = BindingOperations.IsDataBound(child, TextBox.TextProperty);
if (pp)
{
((TextBox)child).Text = ((TextBox)child).Text;
hasErr = BindingOperations.GetBindingExpression(child, TextBox.TextProperty).HasError;
System.Collections.ObjectModel.ReadOnlyCollection<ValidationError> errors = BindingOperations.GetBindingExpression(child, TextBox.TextProperty).ValidationErrors;
if (hasErr)
{
main.BottomText.Foreground = Brushes.Red;
main.BottomText.Text = BindingOperations.GetBinding(child, TextBox.TextProperty).Path.Path.Replace('.', ' ') + ": " + errors[0].ErrorContent.ToString();
return false;
}
}
}
if (child is DatePicker)
{
...
}
}
return true;
}