Как обработать исключение в преобразователе значений, чтобы можно было отобразить пользовательское сообщение об ошибке

#wpf #data-binding #ivalueconverter

#wpf #привязка к данным #ivalueconverter

Вопрос:

У меня есть текстовое поле, которое привязано к классу со свойством типа Timespan, и я написал преобразователь значений для преобразования строки во TimeSpan.

Если в текстовое поле введено не число, я бы хотел, чтобы отображалось пользовательское сообщение об ошибке (а не «строка ввода по умолчанию в неправильном формате»).

Код преобразователя:

     public object ConvertBack(
        object value,
        Type targetType,
        object parameter,
        CultureInfo culture)
    {
        try
        {
            int minutes = System.Convert.ToInt32(value);
            return new TimeSpan(0, minutes, 0);
        }
        catch
        {
            throw new FormatException("Please enter a number");
        }
    }
  

Я установил ‘ValidatesOnExceptions = True’ в привязке XAML.

Однако я наткнулся на следующую статью MSDN, в которой объясняется, почему вышеуказанное не сработает:

«Механизм привязки данных не улавливает исключения, которые генерируются пользовательским преобразователем. Любое исключение, генерируемое методом Convert, или любые неперехваченные исключения, генерируемые методами, вызываемыми методом Convert, рассматриваются как ошибки времени выполнения»

Я прочитал, что ‘ValidatesOnExceptions перехватывает исключения в преобразователях типов, поэтому мои конкретные вопросы:

  • Когда бы вы использовали TypeConverter вместо ValueConverter
  • Предполагая, что TypeConverter не является ответом на проблему выше, как я могу отобразить мое пользовательское сообщение об ошибке в пользовательском интерфейсе

Комментарии:

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

Ответ №1:

Для этого я бы использовал ValidationRule , так конвертер может быть уверен, что преобразование сработает, поскольку оно вызывается только в случае успешной проверки, и вы можете использовать прикрепленное свойство, Validation.Errors которое будет содержать ошибки, которые вы ValidationRule создаете, если ввод не тот, который вы хотите.

например (обратите внимание на привязку всплывающей подсказки)

 <TextBox>
    <TextBox.Style>
        <Style TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="True">
                    <Setter Property="Background" Value="Pink"/>
                    <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
    <TextBox.Text>
        <Binding Path="Uri">
            <Binding.ValidationRules>
                <vr:UriValidationRule />
            </Binding.ValidationRules>
            <Binding.Converter>
                <vc:UriToStringConverter />
            </Binding.Converter>
        </Binding>
    </TextBox.Text>
</TextBox>
  

скриншот

Комментарии:

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

2. @Will: Я думаю, что преобразователи не должны выдавать никаких исключений, если у вас есть пользовательское правило проверки; когда данные поступают из модели, они должны быть действительными в любом случае, и если пользователь вводит неверные данные, конвертер не должен вызываться, поскольку ValidationRule не пропускает их.

3. @H.B.: Но иногда, когда данные поступают из модели, они являются недопустимыми. Без возможности обработки исключений в преобразователе вам придется выполнять проверку, связанную с пользовательским интерфейсом, в ViewModel или в ваших моделях. Например, представление желает отобразить файл в виде XML-файла и поэтому должно прочитать текст из файла и преобразовать его в XML-документ. Но что происходит, когда файл не является допустимым XML-файлом? Следует ли возложить ответственность за проверку этого состояния для представления на ViewModel? Нарушает работу MVVM.

4. @Will: Не кажется такой уж большой проблемой, вы все еще можете добавить ExceptionValidationRule, нет?

5. @H.B.: Не работает для исключений, создаваемых в преобразователях. А теперь мы вернулись к моему первоначальному комментарию….

Ответ №2:

Я использовал проверку и конвертер для приема null и чисел

XAML:

 <TextBox x:Name="HeightTextBox" Validation.Error="Validation_Error">
    <TextBox.Text>
        <Binding Path="Height"
                 UpdateSourceTrigger="PropertyChanged" 
                 ValidatesOnDataErrors="True"
                 NotifyOnValidationError="True"
                 Converter="{StaticResource NullableValueConverter}">
            <Binding.ValidationRules>
                <v:NumericFieldValidation />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>
  

Код, лежащий в основе:

 private void Validation_Error(object sender, ValidationErrorEventArgs e)
{
    if (e.Action == ValidationErrorEventAction.Added)
        _noOfErrorsOnScreen  ;
    else
        _noOfErrorsOnScreen--;
}

private void Confirm_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = _noOfErrorsOnScreen == 0;
    e.Handled = true;
}
  

ValidationRule :

 public class NumericFieldValidation : ValidationRule
{
    private const string InvalidInput = "Please enter valid number!";

    // Implementing the abstract method in the Validation Rule class
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        float val;
        if (!string.IsNullOrEmpty((string)value))
        {
            // Validates weather Non numeric values are entered as the Age
            if (!float.TryParse(value.ToString(), out val))
            {
                return new ValidationResult(false, InvalidInput);
            }
        }

        return new ValidationResult(true, null);
    }
}
  

Конвертер :

 public class NullableValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (string.IsNullOrEmpty(value.ToString()))
            return null;
        return value;
    }
}
  

Комментарии:

1. Спасибо за это комплексное решение! Для всех, у кого возникли проблемы с запуском Validation_Error, убедитесь, что вы включили NotifyOnValidationErrors в XAML. Поскольку у меня уже была проверка ошибок в стиле IDataErrorInfo (и, следовательно, ValidateOnDataErrors), я просмотрел XAML и пропустил эту деталь.

2. @Тим Бадди, это ошибка NotifyOnValidationError, А не ошибка NotifyOnValidationErrors

Ответ №3:

Вы не должны создавать исключения из преобразователя. Я бы внедрил IDataErrorInfo и внедрил ошибку и строку для этого. Пожалуйста, проверьте https://web.archive.org/web/20110528131712/http://www.codegod.biz/WebAppCodeGod/WPF-IDataErrorInfo-and-Databinding-AID416.aspx . HTH daniell

Комментарии:

1. Спасибо Daniell, но я хочу сообщать об ошибках исключения, а не об ошибках в связанном объекте. Я считаю, что IDataErrorInfo допускает только последнее