开发者

WPF: How To Implement Extensive Data-Validation in MVVM [duplicate]

开发者 https://www.devze.com 2023-03-20 11:48 出处:网络
This question already has answers here: Closed 11 years ago. Possible Duplicate: How can I handle a Validation.Error in my ViewModel instead of my View's code behind?
This question already has answers here: Closed 11 years ago.

Possible Duplicate:

How can I handle a Validation.Error in my ViewModel instead of my View's code behind?

The usual examples of how to use Data-Validation in WPF usually only involves defining an error template for the control and displaying the error message in the control tooltip. What I want to do is to create a collection of all ValidationErrors, display it in an ItemsControl to the user and focus the invalid control when the user clicks on the associated (error) item in the ItemsControl.

In non-MVVM projects that ain't much of a problem since I can invoke Validation.GetErrors(obj) on every control and build my collection with that. But in the MVVM the ViewModel has no direct link to the View and therefore I cannot invoke GetErrors() in the ViewModel to build my collection, since I do not have any reference to the开发者_运维问答 controls on the View.

Is there any trick to bind or route the ValidationErrors from the View to the ViewModel or is this just impossible to achieve in the MVVM?


The usual approach for data validation and mvvm is using IDataErrorInfo. You will find a lot information on the web.

The trick to setting the focus to a control you bind to, is to use the Binding property name. Josh Smith posted has blogged about it.


Below is the code for the ValidationErrors property of our ViewModel base class. Our view model base class implements INotifyPropertyChanged and IDataErrorInfo and for Silverlight 4+ also INotifyDataErrorInfo.

Please note the conditional compile statements for Silverlight and WPF. For Silverlight we simply inform the view using the standard INotifyDataErrorInfo mechanism and for WPF we trigger a PropertyChanged event to refresh the affected controls.

The usage is pretty simple. Each derived view model class only needs to set the ValidationErrors property and everything else is taken care of by the property setter. The ValidationFailure failure class belongs to the FluentValidation library that we use to implement the actual validation logic.

public IList<ValidationFailure> ValidationErrors
{
  get { return GetPropertyValue(() => ValidationErrors); }
  protected set
  {
    List<string> obsoleteValidationErrors = null;

    // collect names of properties that do not longer have errors
#if SILVERLIGHT
    if (ErrorsChanged != null)
#else
    if (PropertyChanged != null)
#endif
    {
      var oldErrorsCollection = ValidationErrors != null && ValidationErrors.Count > 0 ? ValidationErrors : new List<ValidationFailure>();
      var newErrorsCollection = value != null && value.Count > 0 ? value : new List<ValidationFailure>();
      var newPropertyNames = newErrorsCollection.Select(x => x.PropertyName).Distinct().ToDictionary(x => x);

      // figure out which errors are no longer part of the new validation error collection
      obsoleteValidationErrors = oldErrorsCollection.Where(x =>
        !newPropertyNames.ContainsKey(x.PropertyName)).Select(x => x.PropertyName).Distinct().ToList();
    }

    if (SetPropertyValue(() => ValidationErrors, value))
    {
      // fire event for properties that do not longer have errors
      if (obsoleteValidationErrors != null)
      {
        foreach (var obsoleteValidationErrorPropertyName in obsoleteValidationErrors)
#if SILVERLIGHT
          ErrorsChanged(this, new DataErrorsChangedEventArgs(obsoleteValidationErrorPropertyName));
#else
          OnPropertyChanged(obsoleteValidationErrorPropertyName);
#endif
      }

      // fire event for properties that now have errors
#if SILVERLIGHT
      if (value != null && ErrorsChanged != null)
#else
      if (value != null && PropertyChanged != null)
#endif
      {
        var propertyNames = value.Select(x => x.PropertyName).Distinct().ToList();

        foreach (var failedProperty in propertyNames)
#if SILVERLIGHT
          ErrorsChanged(this, new DataErrorsChangedEventArgs(failedProperty));
#else
          OnPropertyChanged(failedProperty);
#endif
      }
    }
  }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消