Проверка привязки при первой загрузке - PullRequest
11 голосов
/ 21 марта 2012

Я все еще борюсь с проверкой в ​​WPF.

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

<TextBox local:Masking.Mask="^[a-zA-Z0-9]*$" x:Name="CameraIdCodeTextBox" Grid.Row="1" Grid.Column="1">
  <Binding Path="CameraIdCode" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True" ValidatesOnExceptions="True">
    <Binding.ValidationRules>
      <localValidation:RequiredFieldRule />
    </Binding.ValidationRules>
  </Binding>
</TextBox>

Проблема в том, что при первой загрузке окна в TextBox нет текста (как и следовало ожидать). Но свойство Text связывается со свойством ViewModel, и, как таковое, запускается правило проверки, указывающее на наличие проблемы с окном - еще до того, как у пользователя появилась возможность нарушить бизнес-правило.

Это проблема, которая была решена раньше? Я не мог быть первым, кто испытал это. Я уверен, что это ловушка для молодых игроков.

Ответы [ 2 ]

0 голосов
/ 14 июня 2013

Прошло много времени, и я должен был обновить этот вопрос. Я решил это, используя класс, который я нашел в книге WPF Яна Гриффса (книга О'Рейли):

public static class Validator
{
    /// <summary>
    /// This method forces WPF to validate the child controls of the control passed in as a parameter.
    /// </summary>
    /// <param name="parent">Type: DependencyObject. The control which is the descendent root control to validate.</param>
    /// <returns>Type: bool. The validation result</returns>
    public static bool IsValid(DependencyObject parent)
    {
        // Validate all the bindings on the parent
        bool valid = true;
        LocalValueEnumerator localValues = parent.GetLocalValueEnumerator();
        while (localValues.MoveNext())
        {
            LocalValueEntry entry = localValues.Current;
            if (BindingOperations.IsDataBound(parent, entry.Property))
            {
                Binding binding = BindingOperations.GetBinding(parent, entry.Property);
                foreach (ValidationRule rule in binding.ValidationRules)
                {
                    ValidationResult result = rule.Validate(parent.GetValue(entry.Property), null);
                    if (!result.IsValid)
                    {
                        BindingExpression expression = BindingOperations.GetBindingExpression(parent, entry.Property);
                        Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
                        valid = false;
                    }
                }
            }
        }

        // Validate all the bindings on the children
        for (int i = 0; i != VisualTreeHelper.GetChildrenCount(parent); ++i)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            if (!IsValid(child))
            {
                valid = false;
            }
        }

        return valid;
    }

}

Тогда, по мнению, у меня была следующая конфигурация:

<TextBox local:Masking.Mask="^[0-9]*$" IsEnabled="{Binding Path=LocationNumberEnabled}" Grid.Row="1" Grid.Column="3">
    <Binding Path="LocationNumber"  Mode="TwoWay" UpdateSourceTrigger="LostFocus" NotifyOnValidationError="True" ValidatesOnExceptions="True">
        <Binding.ValidationRules>
            <localValidation:PositiveNumberRule />
            <localValidation:RequiredFieldRule />
        </Binding.ValidationRules>
    </Binding>                    
</TextBox>

работал как шарм! Я просто вызывал метод IsValid каждый раз, когда хотел вручную проверить, например, на кнопку нажмите.

0 голосов
/ 04 июня 2013

Для этого есть пара шаблонов. Я обычно реализую в классе / модели интерфейс ISupportInitialize, что потребует от вас создания BeginInit() и EndInit() в этих методах. Я просто устанавливаю private bool _isInitializing в значение true или false.

В модели представления или где / когда вы создаете / заполняете вашу модель / класс, заключите ее в начало и конец init:

var o = new SampleObject();
o.BeginInit()
o.StartDate = DateTime.Now; //just some sample property...
o.EndInit();

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

В последнее время я использую валидаторы атрибутов, которые запускаются на PropertyChanged, чтобы вы могли сделать что-то вроде:

[CustomValidator("ValidateStartDate")]
 public DateTime StartDate
 { get ...
 {
   set
     {
       if(_startDate == value) return;
       _startDate = value;
       if(_isInitializing) return;
       RaisePropertyChange(() => StartDate);
      }..

Если вы не хотите беспокоиться о ISupportInitialize, тогда передайте все значения, которые вам нужны, в ваших свойствах во время строительства, а не в собственности. Binding сначала запросит геттеры о ваших свойствах и получит их значения, а после чего все пройдет через установщик свойств и будет проверен:

 //c-tor
 public MyObject(DateTime start)
 {
    _startDate = start;
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...