using FluentValidation; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using ValidationResult = System.Windows.Controls.ValidationResult; namespace YY.Admin.Core.FluentValidation { internal class FluentAutoValidationRule : ValidationRule, IFluentAutoValidationRule where T : class { public IValidator? Validator { get; set; } /// /// 当前绑定控件对应的属性名 /// public string? PropertyName { get; set; } // 👇 ValidationStep.UpdatedValue为true: 让Validate方法中value参数为BindingExpression类型,而非值类型 public FluentAutoValidationRule() : base(ValidationStep.UpdatedValue, true) { } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { if (Validator == null || string.IsNullOrEmpty(PropertyName)) return ValidationResult.ValidResult; // 如果 value 不是 BindingExpression,先尝试判断是否是 BindingGroup (兼容性保护) if (value is not BindingExpression bindingExpr) { // 有时 WPF 在不同阶段会传入 BindingGroup 或其他类型,直接跳过初始化阶段的验证,避免误报 return ValidationResult.ValidResult; } // --- 更可靠地判断“初始化阶段的第一次验证” --- // 如果绑定目标尚未挂到视觉树或控件未 Loaded,则视为初始化,不执行验证 var target = bindingExpr.Target as DependencyObject; if (target != null) { // 1) 如果是 FrameworkElement 并且还没 Loaded,则跳过 if (target is FrameworkElement fe && !fe.IsLoaded) return ValidationResult.ValidResult; // 2) 如果 PresentationSource 为空,也说明还没连接到可视树,跳过 var ps = System.Windows.PresentationSource.FromVisual(target as System.Windows.Media.Visual); if (ps == null) return ValidationResult.ValidResult; // 3) 另外,如果控件不可见,也可能是初始化阶段,可根据需要跳过 if (target is UIElement ui && !ui.IsVisible) return ValidationResult.ValidResult; } var dataItem = bindingExpr.DataItem; if (dataItem == null) return new ValidationResult(false, "绑定源为空"); // 解析 Path,例如 "SysUser.Account" var path = bindingExpr.ParentBinding.Path?.Path ?? string.Empty; var rootPropName = path.Split('.')[0]; // SysUser var modelProp = dataItem.GetType().GetProperty(rootPropName); if (modelProp == null) return ValidationResult.ValidResult; var modelObj = modelProp.GetValue(dataItem); if (modelObj is not T model) return ValidationResult.ValidResult; // 调用 FluentValidation 验证指定属性 var result = Validator.ValidateAsync(model, opts => opts.IncludeProperties(PropertyName)) .ConfigureAwait(false) // 同步等待Task(ValidationRule 不支持 async → 只能同步等待。) .GetAwaiter() .GetResult(); var error = result.Errors.FirstOrDefault(e => e.PropertyName == PropertyName); if (error != null) return new ValidationResult(false, error.ErrorMessage); return ValidationResult.ValidResult; } } }