#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 допускает только последнее