IDataErrorInfo接口是如何工作的?

9

我目前正在研究WPF应用程序的验证,并看到了 IDataErrorInfo 的提及。然而,关于如何使用它的指南很少,更糟糕的是没有人解释它的工作原理。

在MSDN.com网站上,给出了以下信息: MSDN

public class Person : IDataErrorInfo
{
    private int age;
    public int Age
    {
        get { return age; }
        set { age = value; }
    }

    public string Error
    {
        get
        {
            return null;
        }
    }

    public string this[string name]
    {
        get
        {
            string result = null;
            if (name == "Age")
            {
                if (this.age < 0 || this.age > 150)
                {
                    result = "Age must not be less than 0 or greater than 150.";
                }
            }
            return result;
        }
    }
}

我明白这里正在发生什么,但我不知道它到底对我的数据做了什么。
这两个属性何时使用?假设有人将“Age”设置为400:那么属性的setter就会被调用。错误提示会阻止它被设置吗?如果不是,它只是警告数字不正确,有什么方法可以防止别人将信息保存为原样?没有“IsValid()”方法来检查,对吧?
很想知道幕后发生了什么。
2个回答

7
Error属性通常不被使用,但是你必须定义它才能实现接口。 正如decyclone所说,验证无法阻止使用错误值设置属性,但你可以将属性设置为默认值。 让我向你展示我的用法。我有几个TextBox需要验证它们的值。当调用set时,我不想显示一个带有错误信息的MessageBox,我想采取一种“网页”方法:当设置无效值时,我希望TextBox的边框和背景变为红色,并且TextBox的工具提示显示其收到的错误信息。

这是我为TextBox编写的XAML:

<converters:ValidationConverter x:Key="validationConverter"/>
<Style x:Key="TestStepTextBox" TargetType="{x:Type TextBox}">
    <Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBox}">
                <Border x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                    <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="Validation.HasError" Value="true">
                        <Setter Property="ToolTip"
                                Value="{Binding RelativeSource={RelativeSource Self}, 
                                Converter={StaticResource validationConverter}, Path=(Validation.Errors)}"/>
                        <Setter Property="Background" Value="#33FF342D"/>
                        <Setter Property="BorderBrush" Value="#AAFF342D"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<TextBox Name="txtRunAfter" Text="{Binding RunAfter, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}" Style="{DynamicResource TestStepTextBox}"/>
<TextBox Name="txtStopAfter" Text="{Binding StopAfter, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}" Style="{DynamicResource TestStepTextBox}"/>

关于转换器的一个非常重要的注意事项。当我输入无效值并设置好值后,我遇到了异常。不知道是否与UpdateSourceTrigger=PropertyChanged有关,在某些情况下,HasError属性为true但没有设置错误(见链接)。因此,这是转换器的代码:

public class ValidationConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        ReadOnlyObservableCollection<ValidationError> errors = value as ReadOnlyObservableCollection<ValidationError>;
        if (errors == null) return value;
        if (errors.Count > 0)
        {
            return errors[0].ErrorContent;
        }
        return "";
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException("This method should never be called");
    }
}

为了防止无效值保存到我的模型层,我使用相同的方法来检查是否应该提交数据到模型。如果该值无效,我只设置属性并不调用模型中属性的设置。请查看以下代码:
private int _runAfter = 0;
public int RunAfter
{
    get
    {
        return _runAfter;
    }

    set
    {
        if (_runAfter != value)
        {
            _runAfter = value;
            OnPropertyChanged("RunAfter");

            if (validateRunAfter() == null)
                setRunAfter(); //sets the property value to the model layer
        }
    }
}

string IDataErrorInfo.this[string columnName]
{
    get
    {
        string message = null;
        if (columnName == "RunAfter")
            message = validateRunAfter();
        //...
        return message;
    }
}

private string validateRunAfter()
{
    if (value >= _order)
        return "Run After value must be less than its Step Order (#) value.";

    return null;
}

谢谢,我还没有时间仔细检查这个,但如果一切正常,我会给你正确的答案的 ;) - Ingó Vals

3
我所了解的IDataErrorInfo仅用于UI方面。它能够提供一种简单的方法将错误信息绑定到WPF UI上。由于WPF UI会“识别”实现IDataErrorInfo接口的对象,就像INotifyPropertyChanged一样,因此您无需编写额外的代码来在UI中显示错误消息。

还有一点需要注意,它并不能阻止设置错误的值。当WPF UI调用索引器并提供属性名称时,它只会告诉WPF UI特定属性中的值无效。


另外需要补充的是,IDataErrorInfo同样适用于ASP.NET MVC UI,因为它来自System.ComponentModel。参考链接:http://www.asp.net/mvc/tutorials/older-versions/models-(data)/validating-with-the-idataerrorinfo-interface-cs - Sai

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