当我的 IValueConverter 抛出异常时,如何运行 IDataErrorInfo 验证代码?

How do I run IDataErrorInfo validation code when my IValueConverter throws an exception?

我在 WPF window 中验证整数时遇到问题。我的业务对象有一个 Age:int 属性。我想验证 Age >= 5。我看到了如何使用 IDataErrorInfo 并将 ValidatesOnDataErrors 设置为 true 来执行此操作。如果我输入一个数字,那行得通。但是,如果我输入空字符串或 "abc",则不会调用验证代码。这是一个问题,因为我有一个按钮,只有在验证码成功时我才想启用它。

根据我的输出窗口,问题是默认转换器在 ConvertBack 方法中抛出 FormatException。这会阻止代码调用 IDataErrorInfo.this[string columnName] 方法。 (我目前使用默认的 int 转换器)

我已经阅读了一些解决方案,但它们要么不起作用,要么看起来很老套:

  1. 使用 Nullable。这不会阻止异常。
  2. 使用字符串而不是 int。这可行,但它不允许我利用内置的 WPF 功能。感觉像是黑客。
  3. 我想我可以使用我自己的 IValueConverter 来拦截 ConvertBack 调用,但我需要让转换器访问我的业务对象,所以它可以设置某种 IsValid:bool 属性。我不知道如何为转换器提供对我的业务对象的引用。

ConvertBack 抛出异常(例如,用户输入空字符串)时,我如何验证我的业务对象?


这是我的业务对象:

class DContext : INotifyPropertyChanged, IDataErrorInfo
{
    public DContext()
    {
        this._submit = new Commands.btnSubmit(this);
    }

    private readonly ICommand _submit;
    public ICommand Submit { get { return this._submit; } }

    private int _age;
    public int Age
    {
        get { return this._age; }
        set
        {
            if (this._age != value)
            {
                this._age = value;
                this.OnPropertyChanged();
            }
        }
    }

    private string _error;
    public string Error
    {
        get { return this._error; }
    }

    public string this[string columnName]
    {
        get 
        {
            this._error = null;
            if (columnName == "Age" && this._age < 5)
                this._error = "Age must be >= 5";

            return this._error;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberNameAttribute]string propertyName = "")
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

这是我的 WPF:

<StackPanel Orientation="Horizontal" Height="20" VerticalAlignment="Top">
    <TextBlock Text="Age" />
    <TextBox Width="100" Text="{Binding Age,ValidatesOnDataErrors=True}" />
    <Button Content="Submit" Command="{Binding Submit}" />
</StackPanel>

有趣的是,我注意到 ICommand.CanExecute 方法在 IDataErrorInfo.this[string columnName] 方法之前被调用。这意味着 ICommand 正在使用尚未验证的值。这意味着我必须复制我的错误检测代码。

我认为解决方案必须是#2 或#3。我倾向于#3,因为它不会给您的视图模型带来任何混乱。让 "ConvertBack" 方法尝试解析输入,如果解析失败,return 一个无效值(可能是 0 或 -1)。

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string input = value as string;
        int validNumber;
        if (input != null && int.TryParse(input, out validNumber))
            return validNumber;
        else
            return 0;
    }

您的验证应该适用于此,无需其他更改。我能看到使用 #2 的唯一原因是,如果您想要不同的错误消息,例如 "The age field is required" 和 "The age field must be a valid number".

This means that the ICommand is working with values that have not yet been validated. This means that I'll have to duplicate my error detection code.

我不确定为什么有必要这样做。难道你不能在验证运行时就提高 "Command.CanExecuteChanged" 吗?如果没有,那么您可能需要考虑使用 INotifyDataErrorInfo 而不是 IDataErrorInfo,因为它允许您引发 "ErrorsChanged" 事件来触发验证。