如何处理值转换器中的异常,以便显示自定义错误消息

20

我有一个文本框,绑定到一个具有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");
        }
    }
我已在 XAML 绑定中设置了 'ValidatesOnExceptions=True'。
但是,我遇到了以下 MSDN 文章,解释了为什么上述内容不起作用:
“数据绑定引擎不会捕获由用户提供的转换器抛出的异常。由 Convert 方法抛出的任何异常,或者 Convert 方法调用的方法抛出的任何未捕获异常,都将被视为运行时错误。”
我已经阅读过 'ValidatesOnExceptions 在 TypeConverters 中可以捕获异常,因此我的具体问题如下:
  • 什么情况下需要使用 TypeConverter 而不是 ValueConverter
  • 假设 TypeConverter 不是上述问题的答案,如何在 UI 中显示自定义错误消息

WPF数据绑定中的错误应该是透明的,这样一个小错误就不会破坏整个应用或用户界面。您可以记录异常,但尝试执行其他操作将违背数据绑定的设计。 - slugster
3个回答

18
我会使用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>

screenshot


1
不幸的是,这只能单向工作。ValidationRules仅在回程时运行,即从UI到数据源。您无法使用此方法拦截或处理Convert方法中抛出的转换器异常。 - user1228
1
@Will:我认为如果你有自定义验证规则,转换器就不应该抛出任何异常;当数据来自模型时,它应该是有效的,如果用户输入无效数据,则不应调用转换器,因为验证规则不允许这样做。 - H.B.
@H.B.:但有时候,当数据来自模型时,它是无效的。如果转换器没有处理异常的能力,那么您必须在ViewModel或Models中进行UI相关的验证。例如,View希望将文件显示为XML文件,因此必须从文件中读取文本并将其转换为XmlDocument。但是,当文件不是有效的XML文件时会发生什么?检查此状态的责任应该由ViewModel承担吗?这将违反MVVM原则。 - user1228
@Will:看起来并不是很大的问题,你仍然可以添加一个ExceptionValidationRule,不是吗? - H.B.
@H.B.:对于转换器中抛出的异常无效。现在我们回到我的最初评论... - user1228

9

我使用验证器和转换器来接受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;
}

验证规则:

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;
    }
}

感谢您提供这个完整的解决方案!对于任何遇到Validation_Error无法运行的人,请确保在XAML中包含NotifyOnValidationErrors。由于我已经使用了IDataErrorInfo样式错误验证(因此ValidateOnDataErrors),所以我快速浏览了XAML并错过了这个细节。 - Tim
@Tim,老兄,应该是NotifyOnValidationError,而不是NotifyOnValidationErrors。 - Allan Ruin

1

谢谢Daniell,但我想报告异常错误而不是绑定对象中的错误。我相信IDataErrorInfo只允许后者。 - sturdytree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接