如何使用自定义ValidationAttribute和INotifyDataErrorInfo验证另一个属性时触发对一个属性的验证

She*_*dan 5 c# validation wpf validationattribute inotifydataerrorinfo

直到最近,我还使用了IDataErrorInfo接口的自定义扩展版本。我的扩展程序使我可以同时处理多个错误,到目前为止,它为我提供了很好的服务。但是,随着INotifyDataErrorInfo界面的引入,我想我将对其进行试验以查看是否有任何改进。

在阅读了一些在线教程之后,我从中获得了它与各种ValidationAttributes的配合使用System.ComponentModel.DataAnnotations namespace。使用这些,Attribute您可以提供如下基本验证规则:

[MinLength(3, ErrorMessage = "Name must be longer than 3 characters.")]
public string Name
{
    get { return name; }
    set { name = value; NotifyPropertyChanged("Name"); Validate("Name", name); }
}
Run Code Online (Sandbox Code Playgroud)

最初,它看起来还不错,因为错误消息直接插入Valaidation.Errors了Applied ErrorTemplates中可用的集合中。但是,大多数内置的验证规则实际上都是最基本的,我已经习惯于实现涉及其他属性值的复杂验证规则。

因此,我着手寻找一种创建包含多个属性的简单验证规则的方法:必须设置两个或多个字段之一的规则。因此,我声明了一个扩展的类,ValidationAttribute并在在线搜索后找到了一种访问其他属性值的方法。

我敲了一个基本UI,并ErrorTemplate在每个UI上应用了一个自定义TextBox,它显示Validation.Errors了数据绑定属性的集合:

<ControlTemplate x:Key="ErrorTemplate">
    <StackPanel Orientation="Horizontal">
        <Border BorderBrush="#4FFF0000" BorderThickness="1" Margin="0,10">
            <AdornedElementPlaceholder />
        </Border>
        <Image Name="WarningImage" Source="pack://application:,,,/WpfApplication1;component/Images/Warning_16.png" Margin="5,0,0,0" Tag="{Binding}" />
        <Popup PlacementTarget="{Binding ElementName=WarningImage}" Placement="Right" Margin="5,0,0,0" AllowsTransparency="True" IsOpen="True">
            <Border BorderThickness="1" BorderBrush="#4FFF0000" CornerRadius="5" Background="White" Padding="5" Margin="10">
                <Border.Effect>
                    <DropShadowEffect Color="Red" Opacity="0.5" BlurRadius="15" ShadowDepth="0" />
                </Border.Effect>
                <ItemsControl ItemsSource="{Binding}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding ErrorContent}" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Border>
        </Popup>
    </StackPanel>
</ControlTemplate>
Run Code Online (Sandbox Code Playgroud)

在属性Attribute上设置了我的自定义设置后,当没有设置任何属性时Name,我设法通过接口将a添加ValidationResult到了Validation.Errors集合中,但这是问题所在:如果我在TextBox绑定到其他必需属性的另一个es数据之一中添加了一个值,在第一个错误消息TextBox将留在那里。

如果我回到第一个TextBox并键入了一些内容,那么验证就可以了,因此即使我删除了该值,它仍然知道已设置了必需的属性之一。因此,验证代码可以工作,但是问题在于,将属性更改为其他必需属性不会触发该Name属性中的验证。

即使将相同的自定义Attribute应用于其他必需的属性,也会发生相同的事情……每个验证错误仅在输入related时才会清除TextBox。我还尝试了内置CustomValidationAttribute方法,该方法使我们能够调用类中的方法进行验证,但最终结果是相同的。

验证代码有效,但不会从其他必需的属性更改中触发。我什至尝试调用该Validate方法,传入其他属性的名称,但是以连续循环结束。所以问题是,当另一个属性已被验证时,如何触发对一个属性的验证?

And*_*ens 2

From这就是我在包含和属性的类中所做的To。我想验证它From小于或等于To

使用 来应用验证逻辑CustomValidationAttribute,这比创建您自己的验证属性类更容易。您只需告诉它您的类的类型以及要调用的包含验证逻辑的方法的名称(但该方法必须具有特定的签名)。这是我的相关代码:-

    [CustomValidation(typeof(MyModel), "ValidateRange")]
    public double From
    {
        get
        {
            return _from;
        }
        set
        {
            if (_from != value)
            {
                _from = value;
                OnPropertyChanged("From");

                // Validate the other side
                ValidateProperty("To", _to);
            }
        }
    }

    [CustomValidation(typeof(MyModel), "ValidateRange")]
    public double To
    {
        get
        {
            return _to;
        }
        set
        {
            if (_to != value)
            {
                _to = value;
                OnPropertyChanged("To");

                // Validate the other side
                ValidateProperty("From", _from);
            }
        }
    }

    private static ValidationResult ValidateRange(ValidationContext validationContext)
    {
        var model = validationContext.ObjectInstance as MyModel;

        if (model.From > model.To)
        {
            return new ValidationResult("Invalid range");
        }

        return null;
    }
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,一个属性设置器中的代码强制验证“其他”属性,正如您在上一段中提到的那样。它没有理由进入无限循环,除非您的验证代码尝试设置其中一个属性,这将触发对 Validate() 的另一次调用,依此类推。