Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用IDataErrorInfo在ViewModel视图上强制更新验证错误?_C#_Wpf_Validation_Mvvm_Idataerrorinfo - Fatal编程技术网

C# 如何使用IDataErrorInfo在ViewModel视图上强制更新验证错误?

C# 如何使用IDataErrorInfo在ViewModel视图上强制更新验证错误?,c#,wpf,validation,mvvm,idataerrorinfo,C#,Wpf,Validation,Mvvm,Idataerrorinfo,我有一个基于MVVM的窗口,有许多控件,我的模型实现了IDataErrorInfo 还有一个SaveCommand按钮,它通过分析Model.Error属性来执行验证 仅当我更改特定控件的值时,或当我使用PropertyChanged通知该属性的更改时,视图才会在有错误的控件周围显示默认的红色边框 即使我没有触摸控件,如何强制View显示所有验证错误? 我所有的验证绑定都包括ValidatesOnDataErrors=True,NotifyOnValidationError=True 我知道一个

我有一个基于MVVM的窗口,有许多控件,我的模型实现了
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;
    }