【问题标题】:How to trigger validation on one property when another property has been validated, using custom ValidationAttribute and INotifyDataErrorInfo如何在验证另一个属性时触发对一个属性的验证,使用自定义 ValidationAttribute 和 INotifyDataErrorInfo
【发布时间】:2014-09-12 12:12:20
【问题描述】:

直到最近,我一直在使用IDataErrorInfo 接口的自定义扩展版本。我的扩展使我能够同时处理多个错误,到目前为止,它对我很有帮助。但是,随着INotifyDataErrorInfo接口的引入,我想我会尝试一下,看看是否有任何改进。

在学习了一些在线教程后,我开始使用来自System.ComponentModel.DataAnnotations namespace 的各种ValidationAttributes。使用这些Attributes,您可以提供如下基本验证规则:

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

最初,它看起来相当不错,因为错误消息直接插入到应用的ErrorTemplates 中可用的Valaidation.Errors 集合中。但是,大多数内置验证规则都是非常基础的,我习惯于必须实现涉及其他属性值的复杂验证规则。

所以我开始寻找一种方法来创建一个涉及多个属性的简单验证规则:必须设置两个或多个字段之一的规则。所以我声明了一个扩展ValidationAttribute的类,在网上搜索后,找到了访问其他属性值的方法。

我创建了一个基本 UI,其中应用了自定义 ErrorTemplate 到每个 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>

Name 属性上设置我的自定义Attribute 后,当两个属性都未设置时,我设法通过接口将ValidationResult 添加到Validation.Errors 集合中,但问题是:如果我添加了一个值进入绑定到其他必需属性的其他TextBoxes 数据之一,第一个TextBox 中的错误消息将保留在那里。

如果我回到第一个 TextBox 并输入一些内容,那么验证将起作用,因此即使我删除了该值,它仍然知道设置了一个必需的属性。所以验证代码有效,但问题是对其他必需属性的属性更改不会触发Name 属性中的验证。

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

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

【问题讨论】:

  • 您是否可以发布相同的工作样本?我很感兴趣。
  • 哇...这个问题还不够长吗?我只是从网上采购了所有东西……你想看什么?或许我可以为您提供一些链接?
  • 问题有足够的细节来理解相同的,我所寻找的只是一个工作示例代码,这样我就不必重新编写整个代码来模拟相同的内容,我可以直接专注于阻塞问题。
  • 哦,对了……不幸的是,其中涉及的代码数量非常可笑——这就是为什么我一开始就没有包含它的原因。今晚回家后我看看能不能举个更简单的例子。

标签: c# wpf validation validationattribute inotifydataerrorinfo


【解决方案1】:

这就是我所做的,在一个包含 FromTo 属性的类中。我想验证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;
    }

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

【讨论】:

  • 感谢您的回复...这看起来很有希望。我今晚回家后试试看。
  • 在尝试了您的建议后,它似乎根本不起作用。它显示另一个属性的TextBoxErrorTemplates 上的每个属性的验证错误。这只是同样的问题,但相反。
  • @Sheridan 道歉 - 我忘了提到我的模型的基类从 OnPropertyChanged() 事件中调用 ValidateProperty() (事后看来,这可能不是一个好的设计,因为目前尚不清楚这种情况正在发生) .所以在上面的代码中,“From”属性获取器将首先验证“From”属性(内部通过 OnPropertyChanged),然后通过显式调用 ValidateProperty() 来验证“To”属性。
猜你喜欢
  • 2013-01-17
  • 1970-01-01
  • 1970-01-01
  • 2015-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多