Проверка привязки данных WPF и сопутствующие триггеры данных либо приводят к исключению переполнения стека, либо не оказывают никакого влияния - PullRequest
0 голосов
/ 13 ноября 2018

У меня было то, что кажется довольно простым ValidationRule - я пытаюсь подтвердить, что конкретная дата не в будущем. Вот пример кода для иллюстрации проблемы:

public class DateNotInTheFutureValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            var date = (DateTime)value;

            // StackOverflowException here
            var now = DateTime.Now;

            ValidationResult validationResult = null;

            if (date > now)
            {
                validationResult = new ValidationResult(false, "Cannot be in the future");
            }
            else
            {
                validationResult = ValidationResult.ValidResult;
            }

            return validationResult;
        }
    }

Для справки: да, я понимаю, что большая часть этого может быть сведена к одному выражению с использованием троичной формы, но это было более полезно для иллюстрации проблемы.

Я применяю это к DatePicker так:

<DatePicker Name="startDatePicker" HorizontalAlignment="Left" Margin="89,94,0,0" VerticalAlignment="Top">
        <DatePicker.SelectedDate>
            <Binding RelativeSource="{RelativeSource Self}" Path="SelectedDate">
                <Binding.ValidationRules>
                    <validationRules:DateNotInTheFutureValidationRule/>
                </Binding.ValidationRules>
            </Binding>
        </DatePicker.SelectedDate>
    </DatePicker>

У меня также есть следующий стиль для обработки случая, когда правило нарушается:

<Window.Resources>
    <Style TargetType="{x:Type DatePicker}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="BorderBrush" Value="Red"/>
                <Setter Property="BorderThickness" Value="1" />
                <Setter Property="Background" Value="Red" />
                <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                    Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

Странная вещь: когда я выбираю дату в будущем в DatePicker, она работает как ожидалось - она ​​работает без исключения и возвращает правильный ValidationResult, указывающий, что это неверно. Однако триггеры не работают - я просто получаю следующее сообщение об ошибке:

Невозможно получить значение «Item []» (тип «ValidationError») из «(Validation.Errors)» (тип «ReadOnlyObservableCollection`1 '). BindingExpression: Путь = (0) [0] .ErrorContent; DataItem = 'DatePicker' (Name = 'startDatePicker'); Целевым элементом является DatePicker (Name = 'startDatePicker'); Свойство target - «ToolTip» (тип «Object»). ArgumentOutOfRangeException: «System.ArgumentOutOfRangeException: указанный аргумент находится вне диапазона допустимых значений. Имя параметра: индекс '

Новые элементы стиля не применяются, даже если я жестко закодирую установщик для всплывающей подсказки в какое-то текстовое значение вместо привязки данных. Однако этот код взят почти напрямую из документации; также сообщения, которые я прочитал об этой ошибке, указывают на то, что эта ошибка должна возникать только тогда, когда у вас нет ошибок проверки, а не когда вы делаете . Почему это происходит?

Если я удаляю строку, устанавливающую ToolTip, я больше не получаю ошибку, но ни один из других сеттеров не имеет никакого эффекта - DatePicker выглядит точно так же, как и раньше.

Однако, если я установил DatePicker на дату в прошлом, я получаю исключение переполнения стека при попытке вызвать DateTime.Now.

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

Как говорится, почему это происходит? Как вызов DateTime.Now может привести к рекурсивному вызову метода Validate? И почему это происходит только тогда, когда я пропускаю свидание в прошлом?

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

Вы привязываете свойство SelectedDate DatePicker к себе.(Это может показаться вероятной причиной переполнения стека ...)

WPF обычно выполняется с использованием MVVM, где связанные значения находятся во ViewModel, попробуйте это.

0 голосов
/ 14 ноября 2018

Оператор ниже запустит бесконечный цикл, потому что вы привязываете свойство к себе (SelectedDate)

<Binding RelativeSource="{RelativeSource Self}" Path="SelectedDate">

Если у вас есть член datacontext (VM), вы можете связать его с ним.

В противном случае вы можете использовать существующее свойство, например «Tag», как показано ниже, чтобы оно работало

<Binding RelativeSource="{RelativeSource Self}" Path="Tag">

Надеюсь, это поможет.

...