С# WPF OnPropertyChange не работает при использовании ValidationRules

Я искал по всему Google и все еще пытаюсь найти правильный ответ. Проблема заключается в следующем, когда я очищаю это текстовое поле, связанное значение не срабатывает.

Итак, проблема в том, что ограниченное значение этого текстового поля не изменяется, если оно становится пустым, но мое правило проверки обнаруживает это и отправляет предупреждение.

Ниже вы можете найти мой фрагмент XAML, правило проверки принадлежности и упомянутое свойство.

  • XAML-фрагмент

    <TextBox Style = "{StaticResource TextBoxErrorStyle}" 
       Margin = "8 0 0 0"
             Visibility = "{Binding AdditionalSurfaceTreatmentInfoVisibility, 
                          Converter = {StaticResource BoolToVisConverter}}"
      FontSize = "12" MaxLength = "4" Width = "40"
      Height = "25">
        <TextBox.Text>
            <Binding Path = "AdditionalSurfaceTreatmentInfo"
           UpdateSourceTrigger = "PropertyChanged"
           NotifyOnSourceUpdated = "True" Mode = "TwoWay"
           NotifyOnValidationError = "True">
                <Binding.ValidationRules>
          <classes:StandardValidationRule ValidatesOnTargetUpdated = "True"/>
          </Binding.ValidationRules>
      </Binding>
        </TextBox.Text>
    </TextBox>
    
  • Стандартное правило проверки:

    public class StandardValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            var valueToValidate = value as string;
            if (string.IsNullOrEmpty(valueToValidate))
            {
                return new ValidationResult(false, "Field is mandatory.");
            }
            return new ValidationResult(true, null);
        }
    }
    
  • Недвижимость:

    private string _additionalSurfaceTreatmentInfo;
    public string AdditionalSurfaceTreatmentInfo
    {
        get => _additionalSurfaceTreatmentInfo;
        set
        {
            _additionalSurfaceTreatmentInfo = value;
            OnPropertyChanged();
            SetOrModifySurfaceTreatment();
            Console.WriteLine(string.IsNullOrEmpty(_additionalSurfaceTreatmentInfo).ToString());
        }
    }
    

Заранее благодарим вас за ваши усилия. Любая помощь высоко ценится!

Приведенный выше код работает так, как я предпочитаю. Я уже перепробовал все, что касается различных свойств, которые я могу заполнить в ValidationRule. Единственное, что нужно изменить, это то, что когда текстовое поле пусто, оно должно запускать метод OnPropertyChanged(). Таким образом, я могу позже проверить свойство, когда я, например, отправлю команду сохранения.

Я бы использовал DependencyProperty для AdditionalSurfaceTreatementInfo, у меня они всегда работали. Но позвольте мне спросить, дает ли что-нибудь установка точки останова в OnPropertyChanged? Я предполагаю, что нет.

JWP 02.11.2022 18:47

@JWP Прежде всего, спасибо за ваш ответ! У меня нет большого опыта работы со свойствами зависимостей. У вас есть пример или ресурс, который я могу использовать, чтобы попробовать? Я уже пытался установить точку останова на OnPropertyChange, но программе еще нужно достичь этой точки. Я заметил, что если я удаляю ValidatesOnTargetUpdated = "True", событие OnPropertyChange действительно работает.

MarKamp 02.11.2022 19:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используйте свойство ValidationStep. Пример:

<local:StandardValidationRule ValidationStep = "UpdatedValue"
                              ValidatesOnTargetUpdated = "True"/>

Но для проверки исходного свойства нужна более сложная логика, так как валидатор получает не значение свойства, а выражение привязки от целевого свойства.

    public class StandardValidationRule : ValidationRule
    {
        private bool gettingValue = false;
        private bool isValueReceived = false;
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            if (gettingValue)
            {
                isValueReceived = true;
                return ValidationResult.ValidResult;
            }

            string? valueToValidate = value as string;

            ValidationResult? result = null;
            if (valueToValidate is null && value is not null)
            {
                if (value is BindingExpressionBase bindingExpression)
                {
                    gettingValue = true;
                    isValueReceived = false;
                    DependencyObject target = bindingExpression.Target;
                    var gettingValueExpression = BindingOperations.SetBinding(target, SourceValueProperty, bindingExpression.ParentBindingBase);
                    if (!isValueReceived)
                    {
                        gettingValueExpression.UpdateTarget();
                    }
                    valueToValidate = target.GetValue(SourceValueProperty)?.ToString();
                    target.ClearValue(SourceValueProperty);
                    gettingValue = false;
                }
                else
                {
                    result = unvalid;
                }
            }

            if (result is null)
            {
                result = string.IsNullOrEmpty(valueToValidate)
                            ? unvalid
                            : ValidationResult.ValidResult;
            }
            return result;
        }
        private static readonly ValidationResult unvalid = new ValidationResult(false, "Field is mandatory.");



        // Using a DependencyProperty as the backing store for SourceValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SourceValueProperty =
            DependencyProperty.RegisterAttached("SourceValue", typeof(object), typeof(StandardValidationRule), new PropertyMetadata(null));


    }

Есть еще один способ получить исходное значение.
Он основан на использовании метода internal через отражение.
Что многие считают «плохим» способом.
Но работает гораздо эффективнее.
И я думаю вряд ли кто-то будет вносить изменения в метод internal, который уже во многих местах используется.

    public static class BindingExpressionHelper
    {
        private static readonly Func<BindingExpressionBase, DependencyObject, DependencyProperty, object> GetValueOfBindingExpression;

        static BindingExpressionHelper()
        {
            Type beType = typeof(BindingExpressionBase);
            var beMethod = beType
                .GetMethod("GetValue", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { typeof(DependencyObject), typeof(DependencyProperty) })
                ?? throw new Exception("GetValue method not found.");
            var beFunc = (Func<BindingExpressionBase, DependencyObject, DependencyProperty, object>)
                beMethod.CreateDelegate(typeof(Func<BindingExpressionBase, DependencyObject, DependencyProperty, object>));
            GetValueOfBindingExpression = beFunc;
        }

        /// <summary>Returns the source value of this binding expression.</summary>
        /// <param name = "bindingExpression">The binding expression whose value to get.</param>
        /// <returns>The value of the binding expression received.</returns>
        public static object? GetSourceValue(this BindingExpressionBase bindingExpression)
        {
            DependencyObject target = bindingExpression.Target;
            DependencyProperty targetProperty = bindingExpression.TargetProperty;
            var value = GetValueOfBindingExpression(bindingExpression, target, targetProperty);

            return value;
        }
    }
    public class StandardValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            string? valueToValidate = value as string;

            ValidationResult? result = null;
            if (valueToValidate is null && value is not null)
            {
                if (value is BindingExpressionBase bindingExpression)
                {
                    valueToValidate = bindingExpression.GetSourceValue()?.ToString();
                }
                else
                {
                    result = unvalid;
                }
            }

            if (result is null)
            {
                result = string.IsNullOrEmpty(valueToValidate)
                            ? unvalid
                            : ValidationResult.ValidResult;
            }
            return result;
        }
        private static readonly ValidationResult unvalid = new ValidationResult(false, "Field is mandatory.");
    }

Спасибо за ответ! Если я добавлю эту строку, OnPropertyChanged снова будет работать, но стиль ошибки проверки всегда будет оставаться активным, независимо от того, является ли значение нулевым, пробельным, пустым или заполненным.

MarKamp 02.11.2022 19:06

@KamphuisCoding, прости! Я забыл о том, что при проверке источника приходит не значение свойства. Я дополнил свой ответ реализацией проверки источника.

EldHasp 02.11.2022 20:41

Привет @EldHasp, нет проблем, приятель. Решения, которые вы указали, работают как шарм! Еще раз большое спасибо за ваше время и усилия! Из любопытства, какое из двух решений вы бы реализовали сами?

MarKamp 03.11.2022 09:52

Второй вариант — с отражением. Но я бы хорошо это задокументировал, чтобы в случае возникновения проблем (кто знает, какие еще изменения они могут внести в .Net), пользователи понимали причину проблемы и знали, как ее решить.

EldHasp 03.11.2022 11:54

Другие вопросы по теме