首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >父子ValidationRule

父子ValidationRule
EN

Stack Overflow用户
提问于 2012-08-04 04:32:11
回答 1查看 558关注 0票数 1

我有一个使用ValidationRules的复杂场景,我需要一些帮助。我有一个大致组织如下的用户控件:

代码语言:javascript
复制
Parent (ItemsControl)
    Child 1
        Property 1
        Property 2
    Child 2
        Property 1
        Property 2

当Child 1.Property 1更改时,我需要对其执行验证。但是,验证规则需要Child 1.Property 1的值以及其所有同级(变量number)的属性1的值才能执行验证。我可以在父ItemsControl上放置一个ValidationRule,但我需要绑定到Child1.Property1的控件来显示错误。目前,当我将验证放在父级上时,错误显示在父级上,而不是子级上。我也考虑过使用BindingGroups,但我希望在属性更改时自动触发验证。据我所知,没有一种方法可以自动强制为BindingGroup触发验证。

有没有办法完成我想要做的事情?

EN

回答 1

Stack Overflow用户

发布于 2019-12-06 04:53:25

这会自动将对象的所有数据注释属性转换为ValidationRule,并且可以在所有TextBoxes上的应用程序中应用一次:

代码语言:javascript
复制
using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

using DaValidationResult = System.ComponentModel.DataAnnotations.ValidationResult;
using WinValidationResult = System.Windows.Controls.ValidationResult;

public sealed class DataAnnotationsBehavior
{
  public static bool GetValidateDataAnnotations(DependencyObject obj) =>
    (bool)obj.GetValue(ValidateDataAnnotationsProperty);
  public static void SetValidateDataAnnotations(DependencyObject obj, bool value) =>
    obj.SetValue(ValidateDataAnnotationsProperty, value);

  public static readonly DependencyProperty ValidateDataAnnotationsProperty =
      DependencyProperty.RegisterAttached("ValidateDataAnnotations", typeof(bool), 
        typeof(DataAnnotationsBehavior), new PropertyMetadata(false, OnPropertyChanged));

  private static void OnPropertyChanged(DependencyObject d, 
    DependencyPropertyChangedEventArgs e)
  {
    var boolean = (bool)e.NewValue;
    if (!(d is TextBox textBox))
      throw new NotSupportedException(
        @$"The behavior " +
        "'{typeof(DataAnnotationsBehavior)}' can only be applied " +
        "on elements of type '{typeof(TextBox)}'.");

    var bindingExpression =
      textBox.GetBindingExpression(TextBox.TextProperty);

    if (boolean)
    {
      var dataItem = bindingExpression.DataItem;
      if (bindingExpression.DataItem == null)
        return;

      var type = dataItem.GetType();
      var prop = type.GetProperty(bindingExpression.ResolvedSourcePropertyName);
      if (prop == null)
        return;

      var allAttributes = prop.GetCustomAttributes(typeof(ValidationAttribute), true);
      foreach (var validationAttr in allAttributes.OfType<ValidationAttribute>())
      {
        var context = new ValidationContext(dataItem, null, null) 
        { MemberName = bindingExpression.ResolvedSourcePropertyName };

        bindingExpression
          .ParentBinding
          .ValidationRules
          .Add(new AttributesValidationRule(context, validationAttr));
      }
    }
    else
    {
      var das =
        bindingExpression
          .ParentBinding
          .ValidationRules
          .OfType<AttributesValidationRule>()
          .ToList();

      if (das != null)
        foreach (var da in das)
          bindingExpression.ParentBinding.ValidationRules.Remove(da);
    }
  }

  abstract class DaValidationRule : ValidationRule
  {
    public ValidationContext ValidationContext { get; }

    public DaValidationRule(ValidationContext validationContext)
    {
      ValidationContext = validationContext;
    }
  }

  class AttributesValidationRule : DaValidationRule
  {
    public ValidationAttribute ValidationAttribute { get; }

    public AttributesValidationRule(ValidationContext validationContext, 
      ValidationAttribute attribute)
      : base(validationContext) =>
      ValidationAttribute = attribute;

    public override WinValidationResult Validate(object value, CultureInfo cultureInfo)
    {
      var result = ValidationAttribute.GetValidationResult(value, ValidationContext);

      return result == DaValidationResult.Success
        ? WinValidationResult.ValidResult
        : new WinValidationResult(false, result.ErrorMessage);
    }
  }
}

用法:

代码语言:javascript
复制
<Window.DataContext>
  <local:ViewModel />
</Window.DataContext>
<Window.Resources>
  <ResourceDictionary>
    <Style TargetType="TextBox">
      <Setter 
        Property="local:DataAnnotationsBehavior.ValidateDataAnnotations" 
        Value="True"/>
      <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
          <Setter 
            Property="ToolTip" 
            Value="{Binding (Validation.Errors)[0].ErrorContent,
              RelativeSource={RelativeSource Self}}"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </ResourceDictionary>
</Window.Resources>
<StackPanel DataContext="{Binding Model}">
  <TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>

我让DaValidationRule类变得抽象,因为我想我将来可能想要扩展它,这样它也涵盖了IValidationAttribute,也许还涵盖了其他场景。

有关编辑模式下DataGridTextColumn中的TextBoxes的版本,以及完整代码,请参阅this

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11802872

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档