我在使用ValidationRules时遇到了复杂的情况,我需要一些帮助。我有一个大致这样组织的用户控件:
Parent (ItemsControl)
Child 1
Property 1
Property 2
Child 2
Property 1
Property 2
[Child 1.Property 1更改后,我需要对其进行验证。但是,验证规则要求其子级(所有变量)的子代1,属性1的值以及属性1的值来执行验证。我可以将ValidationRule放在Parent ItemsControl上,但是我需要绑定到Child1.Property1的控件来显示错误。当前,当我将验证放在父级上时,错误显示在父级上,而不是子级上。我也考虑过使用BindingGroups,但我希望更改属性后自动触发验证。据我所知,没有一种方法可以自动为绑定组强制验证。
是否有办法完成我想做的事情?
这是我的解决方法:
namespace WpfValidationRules
{
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.Xaml.Behaviors;
using DaValidationResult = System.ComponentModel.DataAnnotations.ValidationResult;
using WinValidationResult = System.Windows.Controls.ValidationResult;
public class DataAnnotationsBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
var dataItem = Binding.DataItem;
if (Binding.DataItem == null)
return;
var type = dataItem.GetType();
var prop = type.GetProperty(Binding.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 = Binding.ResolvedSourcePropertyName };
Binding.ParentBinding.ValidationRules.Add(new AttributesValidationRule(context, validationAttr));
}
}
protected override void OnDetaching()
{
var das = Binding
.ParentBinding
.ValidationRules
.OfType<AttributesValidationRule>()
.ToList();
if (das != null)
foreach (var da in das)
Binding.ParentBinding.ValidationRules.Remove(da);
}
protected BindingExpression Binding => AssociatedObject.GetBindingExpression(TextBox.TextProperty);
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);
}
}
}
}
用法:
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<b:Interaction.Behaviors>
<local:DataAnnotationsBehavior />
</b:Interaction.Behaviors>
</TextBox>