C# 如何使用IDataErrorInfo在ViewModel视图上强制更新验证错误?
我有一个基于MVVM的窗口,有许多控件,我的模型实现了C# 如何使用IDataErrorInfo在ViewModel视图上强制更新验证错误?,c#,wpf,validation,mvvm,idataerrorinfo,C#,Wpf,Validation,Mvvm,Idataerrorinfo,我有一个基于MVVM的窗口,有许多控件,我的模型实现了IDataErrorInfo 还有一个SaveCommand按钮,它通过分析Model.Error属性来执行验证 仅当我更改特定控件的值时,或当我使用PropertyChanged通知该属性的更改时,视图才会在有错误的控件周围显示默认的红色边框 即使我没有触摸控件,如何强制View显示所有验证错误? 我所有的验证绑定都包括ValidatesOnDataErrors=True,NotifyOnValidationError=True 我知道一个
IDataErrorInfo
还有一个SaveCommand
按钮,它通过分析Model.Error
属性来执行验证
仅当我更改特定控件的值时,或当我使用PropertyChanged通知该属性的更改时,视图才会在有错误的控件周围显示默认的红色边框
即使我没有触摸控件,如何强制View显示所有验证错误?
我所有的验证绑定都包括ValidatesOnDataErrors=True,NotifyOnValidationError=True
我知道一个解决方案是使用一个包含所有错误的聚合框,但我更喜欢在每个控件的基础上显示错误
我不想为ViewModel中的每个绑定属性触发Model.NotifyPropertyChanged
我使用的是WPF 4.0,而不是Silverlight,因此
INotifyDataErrorInfo
不起作用。到目前为止,我找到的最佳解决方案是将DataContext更改为null并返回到ViewModel实例
这会触发视图上绑定了DataContext
的控件的更新,该视图绑定了InnerViewModel
:
public void ForceUpdateErrors() {
var tmpInnerVM = _mainViewModel.InnerViewModel;
_mainViewModel.InnerViewModel = null;
_mainViewModel.InnerViewModel = tmpInnerVM;
}
建议检查此技巧后是否没有数据丢失。我曾经遇到过这样一种情况,该代码触发了ComboBox.SelectedItem的源代码更新,但我成功地解决了这个问题。这是由于使用基于资源的BindingProxy和跨控制层次结构的
DataContext=null
传播顺序造成的。您提到您不希望为绑定到的属性引发属性更改,但这确实是实现这一点的最简单方法。调用不带参数的PropertyChanged将为viewmodel中的所有属性引发
或者,您可以更新任何控件上的绑定(并强制重新验证),如下所示:
myControl.GetBindingExpression(ControlType.ControlProperty).UpdateSource();
这个“黑客”暂时对我有效,为了强制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);
如果(子对象是文本框)
{
bool pp=BindingOperations.IsDataBound(子级,TextBox.TextProperty);
如果(pp)
{
((TextBox)child.Text=((TextBox)child.Text;
hasErr=BindingOperations.GetBindingExpression(子级,TextBox.TextProperty).HasError;
System.Collections.ObjectModel.ReadOnlyCollection errors=BindingOperations.GetBindingExpression(子级,TextBox.TextProperty)。ValidationErrors;
如果(哈塞尔)
{
main.BottomText.前台=画笔.红色;
main.BottomText.Text=BindingOperations.GetBinding(子级,TextBox.TextProperty).Path.Path.Replace('.','')+“:“+错误[0]。ErrorContent.ToString();
返回false;
}
}
}
if(子对象是日期选择器)
{
...
}
}
返回true;
}
感谢您使用PropertyChanged的技巧。我不知道这是可能的。我发现了关于这个话题的另一个讨论:如果有人感兴趣。如果有人有一个简单的viewModel,这是一个很好的答案。但是,我有一个具有嵌套ViewModels的复杂视图,因此我必须编写代码,为实现InotifyPropertyChanged的每个嵌套绑定模型/ViewModel调用PropertyChanged一次。如果只想更新与特定ViewModelmyControl.GetBindingExpression相关的视图的一部分,最好了解这个技巧(ControlType.ControlProperty).UpdateTarget();实际上在不更新源属性的情况下获取最新的验证。
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;
}