Passing state of WPF ValidationRule to View Model in MVVM

前端 未结 9 1714
难免孤独
难免孤独 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:56
    1. Implement IDataErrorInfo in your model or Viewmodel depending logic of binding property. You may implement in both classes.

    2. Implement this too in your base validation class. Here validation will trigger when binding IDataErrorInfo does not work.

      public virtual bool HasError
      {
          get { return _hasError; } 
          set
          {
              // if (value.Equals(_hasError)) return;
              _hasError = value;
              RaisePropertyChanged(() => HasError);
          }
      }
      
    3. Next, add global class

      public class ProtocolSettingsLayout
      {
          public static readonly DependencyProperty MVVMHasErrorProperty = DependencyProperty.RegisterAttached("MVVMHasError", typeof(bool), typeof(ProtocolSettingsLayout), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceMVVMHasError));
      
          public static bool GetMVVMHasError(DependencyObject d)
          {
              return (bool)d.GetValue(MVVMHasErrorProperty);
          }
      
          public static void SetMVVMHasError(DependencyObject d, bool value)
          {
              d.SetValue(MVVMHasErrorProperty, value);
          }
      
          private static object CoerceMVVMHasError(DependencyObject d, Object baseValue)
          {
              bool ret = (bool)baseValue;
      
              if (BindingOperations.IsDataBound(d, MVVMHasErrorProperty))
              {
                  if (GetHasErrorDescriptor(d) == null)
                  {
                      DependencyPropertyDescriptor desc = DependencyPropertyDescriptor.FromProperty(Validation.HasErrorProperty, d.GetType());
                      desc.AddValueChanged(d, OnHasErrorChanged);
                      SetHasErrorDescriptor(d, desc);
                      ret = System.Windows.Controls.Validation.GetHasError(d);
                  }
              }
              else
              {
                  if (GetHasErrorDescriptor(d) != null)
                  {
                      DependencyPropertyDescriptor desc = GetHasErrorDescriptor(d);
                      desc.RemoveValueChanged(d, OnHasErrorChanged);
                      SetHasErrorDescriptor(d, null);
                  }
              }
              return ret;
          }
      
          private static readonly DependencyProperty HasErrorDescriptorProperty = DependencyProperty.RegisterAttached("HasErrorDescriptor",
                                                                                  typeof(DependencyPropertyDescriptor),
                                                                                  typeof(ProtocolSettingsLayout));
      
          private static DependencyPropertyDescriptor GetHasErrorDescriptor(DependencyObject d)
          {
              var ret = d.GetValue(HasErrorDescriptorProperty);
              return ret as DependencyPropertyDescriptor;
          }
      
          private static void OnHasErrorChanged(object sender, EventArgs e)
          {
              DependencyObject d = sender as DependencyObject;
      
              if (d != null)
              {
                  d.SetValue(MVVMHasErrorProperty, d.GetValue(Validation.HasErrorProperty));
              }
          }
      
          private static void SetHasErrorDescriptor(DependencyObject d, DependencyPropertyDescriptor value)
          {
              var ret = d.GetValue(HasErrorDescriptorProperty);
              d.SetValue(HasErrorDescriptorProperty, value);
          }
      }
      
    4. xaml

      <TextBox  PreviewTextInput="NumValidationTextBox" Text="{Binding ESec, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true, NotifyOnValidationError=true, ValidatesOnExceptions=True, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, TargetNullValue='0', FallbackValue='0' }" Validation.ErrorTemplate="{StaticResource validationTemplate}" viewmodels:ProtocolSettingsLayout.MVVMHasError="{Binding Path=HasError}" />    
      
    0 讨论(0)
  • 2021-01-01 20:58

    You must specify custome user control depending bind type property. For example if your property is int type you must place control that not allow diferent value except intenger type.

    The logic you may put in PreviewTextInput="NumberValidationTextBox".

    private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
        { 
            Regex regex = new Regex("[^0-9]+");
            e.Handled = regex.IsMatch(e.Text);
        }
    

    just insert your logic or place custome control and you are done.

    Defently must implement mvvm validation too.

    0 讨论(0)
  • 2021-01-01 21:01

    I encountered the same problem and solved it with a trick. See the converter below:

    public class IntValidationConverter : IValueConverter
    {
        static string[] AllValuse = new string[100000];
        static int index = 1;
        public static int StartOfErrorCodeIndex = -2000000000;
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return null;
            if (value.ToString() == "") return null;
    
            int iValue = (int)(value);
    
            if (iValue == int.MinValue) return null;
    
            if (iValue >= StartOfErrorCodeIndex) return value;
            if ((iValue < IntValidationConverter.StartOfErrorCodeIndex) && (iValue > int.MinValue)) return AllValuse[StartOfErrorCodeIndex - iValue];
    
            return null;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return int.MinValue;
            if (value.ToString() == "") return int.MinValue;
    
            int result;
            bool success = int.TryParse(value.ToString(), out result);
            if (success) return result;
    
            index++;
            AllValuse[index] = value.ToString();
            return StartOfErrorCodeIndex - index;
        }
    }
    
    0 讨论(0)
提交回复
热议问题