【问题标题】:How to force validation errors update on View from ViewModel using IDataErrorInfo?如何使用 IDataErrorInfo 从 ViewModel 强制更新视图上的验证错误?
【发布时间】:2012-04-16 16:41:37
【问题描述】:

我有一个带有许多控件的基于 MVVM 的窗口,我的模型实现了IDataErrorInfo

还有一个SaveCommand 按钮,通过分析Model.Error 属性来执行验证。

只有当我更改特定控件的值,或者当我使用 PropertyChanged 通知该属性的更改时,视图才会在有错误的控件周围显示默认的红色边框。

即使我没有触摸控件,如何强制 View 显示所有验证错误?

我所有的验证绑定都包括ValidatesOnDataErrors=True, NotifyOnValidationError=True

我知道一种解决方案是使用一个包含所有错误的聚合框,但我更愿意在每个控件的基础上显示错误。

我不想为 ViewModel 中的每个绑定属性触发 Model.NotifyPropertyChanged

我使用的是 WPF 4.0,而不是 Silverlight,所以 INotifyDataErrorInfo 不起作用。

【问题讨论】:

    标签: c# wpf validation mvvm idataerrorinfo


    【解决方案1】:

    您提到您不想为绑定到的属性引发属性更改,但这确实是完成此操作的最简单方法。调用不带参数的 PropertyChanged 将引发视图模型中的所有属性。

    或者,您可以像这样更新任何控件上的绑定(并强制重新验证):

    myControl.GetBindingExpression(ControlType.ControlProperty).UpdateSource();
    

    【讨论】:

    • 感谢 PropertyChanged 的​​技巧。我不知道这是可能的。我发现了关于这个主题的另一个讨论:stackoverflow.com/questions/1135012/… 如果有人感兴趣的话。如果有人有一个单一的、简单的 viewModel,这是一个很好的答案。但是,我有一个带有嵌套 ViewModel 的复杂视图,因此我必须编写代码来为每个实现 INotifyPropertyChanged 的​​嵌套绑定模型/视图模型调用一次 PropertyChanged
    • 如果只想更新与特定 ViewModel 相关的视图的一部分,了解这个技巧会很好
    • myControl.GetBindingExpression(ControlType.ControlProperty).UpdateTarget();实际上无需更新源属性即可更新您的验证。
    【解决方案2】:

    到目前为止,我发现的最佳解决方案是将 DataContext 更改为 null 并返回 ViewModel 的实例。

    这会触发将DataContext 绑定到InnerViewModel 的视图上的控件更新:

    public void ForceUpdateErrors() {
        var tmpInnerVM = _mainViewModel.InnerViewModel;
        _mainViewModel.InnerViewModel = null;
        _mainViewModel.InnerViewModel = tmpInnerVM;
    }
    

    建议检查此技巧后是否没有数据丢失。我有一个案例,这个代码触发了 ComboBox.SelectedItem 的源更新,但我设法解决了它。它是由使用基于资源的 BindingProxy 和 DataContext=null 跨控制层次结构传播的顺序引起的。

    【讨论】:

      【解决方案3】:

      这个“黑客”暂时对我有用,强制 InotifyChanged 事件,只需将该控件分配回它自己的内容。在评估绑定的 HasError 函数之前执行此操作。例如一个文本框是:

       ((TextBox)child).Text = ((TextBox)child).Text;
      

      然后是一个完整的例子(在我听说这不是真正的 MVVM 之前,我直接在网格上得到了一个句柄,以便于显示这段代码片段)

              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;
          }
      

      【讨论】:

        猜你喜欢
        • 2012-11-02
        • 1970-01-01
        • 2011-04-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多