Passing state of WPF ValidationRule to View Model in MVVM

前端 未结 9 1717
难免孤独
难免孤独 2021-01-01 20:10

I am stuck in a seemingly common requirement. I have a WPF Prism (for MVVM) application. My model implements the IDataErrorInfo for validation. The

9条回答
  •  独厮守ぢ
    2021-01-01 20:37

    For MVVM I prefer to use Attached Properties for this type of thing because they are reusable and it keeps the view models clean.

    In order to bind to the Validation.HasError property to your view model you have to create an attached property which has a CoerceValueCallback that synchronizes the value of your attached property with the Validation.HasError property on the control you are validating user input on.

    This article explains how to use this technique to solve the problem of notifying the view model of WPF ValidationRule errors. The code was in VB so I ported it over to C# if you're not a VB person.

    The Attached Property

    public static class ValidationBehavior
    {
        #region Attached Properties
    
        public static readonly DependencyProperty HasErrorProperty = DependencyProperty.RegisterAttached(
            "HasError",
            typeof(bool),
            typeof(ValidationBehavior),
            new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceHasError));
    
        private static readonly DependencyProperty HasErrorDescriptorProperty = DependencyProperty.RegisterAttached(
            "HasErrorDescriptor",
            typeof(DependencyPropertyDescriptor),
            typeof(ValidationBehavior));
    
        #endregion
    
        private static DependencyPropertyDescriptor GetHasErrorDescriptor(DependencyObject d)
        {
            return (DependencyPropertyDescriptor)d.GetValue(HasErrorDescriptorProperty);
        }
    
        private static void SetHasErrorDescriptor(DependencyObject d, DependencyPropertyDescriptor value)
        {
            d.SetValue(HasErrorDescriptorProperty, value);
        }
    
        #region Attached Property Getters and setters
    
        public static bool GetHasError(DependencyObject d)
        {
            return (bool)d.GetValue(HasErrorProperty);
        }
    
        public static void SetHasError(DependencyObject d, bool value)
        {
            d.SetValue(HasErrorProperty, value);
        }
    
        #endregion
    
        #region CallBacks
    
        private static object CoerceHasError(DependencyObject d, object baseValue)
        {
            var result = (bool)baseValue;
            if (BindingOperations.IsDataBound(d, HasErrorProperty))
            {
                if (GetHasErrorDescriptor(d) == null)
                {
                    var desc = DependencyPropertyDescriptor.FromProperty(System.Windows.Controls.Validation.HasErrorProperty, d.GetType());
                    desc.AddValueChanged(d, OnHasErrorChanged);
                    SetHasErrorDescriptor(d, desc);
                    result = System.Windows.Controls.Validation.GetHasError(d);
                }
            }
            else
            {
                if (GetHasErrorDescriptor(d) != null)
                {
                    var desc = GetHasErrorDescriptor(d);
                    desc.RemoveValueChanged(d, OnHasErrorChanged);
                    SetHasErrorDescriptor(d, null);
                }
            }
            return result;
        }
        private static void OnHasErrorChanged(object sender, EventArgs e)
        {
            var d = sender as DependencyObject;
            if (d != null)
            {
                d.SetValue(HasErrorProperty, d.GetValue(System.Windows.Controls.Validation.HasErrorProperty));
            }
        }
    
        #endregion
    }
    

    Using The Attached Property in XAML

      
        
          
            
              
                
              
            
         
      
    
    

    Now the property on your view model will be synchronized with Validation.HasError on your textbox.

提交回复
热议问题