WPF使用ValidationRules对MVVM架构数据验证
对于WPF中的验证, View验证实现起来很简单, 可以通道 Validation.ErrorEvent 冒泡传递到View的逻辑树上, 只是, 通常这样做的情况下, 我们需要为View添加事件代码监听这类错误事件, 然后进行处理。
这样做可以说是非常简单, 但是这样的硬编码的, 基本上每个模块每个功能, 你都必不可少的为其进行重复的工作, 这是一项非常枯燥且无聊的体力活!
于是, 则考虑MVVM的架构中, 如何把这种模式传递到ViewModel中, 使得前端的验证, 对于ViewModel仍然有效。
实现的原理, 如图所示:
在此之前, 对于View前端验证需要做的一些操作步骤,
- 1.为验证的属性添加自定义验证类
- 2.设置验证错误的通知属性 NotifyOnValidationError="True" 。 注: 如此一来, 则可以产生Validation.ErrorEvent事件
- 3.通过自定义的 ValidationExceptionBehavior 继承于 Behavior, 用于监听 Validation.ErrorEvent 的错误事件。
- 4.在 ValidationExceptionBehavior 中通过 AssociatedObjectde的DataContex获取到关联当前View的DataContex, 从而改变DataContext的后端验证条件。
1.设置属性自定义的验证类并添加 NotifyOnValidationError="True" 属性
<TextBox Margin="15 0 10 0" Style="{StaticResource MaterialDesignFloatingHintTextBox}" materialDesign:HintAssist.Hint="登录名 *"> <TextBox.Text> <Binding Path="Model.Account" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True"> <Binding.ValidationRules> <domain:CustomizeValidationRule validationType="Str" minLength="3" maxLength="10" errorMessage="输入长度范围 [3-10]字" ValidatesOnTargetUpdated="True" /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
2.自定义 IValidationExceptionHandler 接口, ViewModel继承IValidationExceptionHandler , 用于接收前端的验证结果。
public interface IValidationExceptionHandler { /// <summary> /// 是否有效 /// </summary> bool IsValid { get; set; } }
3. 自定义 ValidationExceptionBehavior, 用于监听处理View的错误事件
/// <summary> /// 验证行为类,可以获得附加到的对象 /// </summary> public class ValidationExceptionBehavior : Behavior<FrameworkElement> { /// <summary> /// 错误计数器 /// </summary> private int _validationExceptionCount = 0; /// <summary> /// 附加对象时 /// </summary> protected override void OnAttached() { //附加对象时,给对象增加一个监听验证错误事件的能力,注意该事件是冒泡的 this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(this.OnValidationError)); } #region 获取实现接口的对象 /// <summary> /// 获取对象 /// </summary> /// <returns></returns> private IValidationExceptionHandler GetValidationExceptionHandler() { if (this.AssociatedObject.DataContext is IValidationExceptionHandler) { var handler = this.AssociatedObject.DataContext as IValidationExceptionHandler; return handler; } return null; } #endregion #region 验证事件方法 /// <summary> /// 验证事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnValidationError(object sender, ValidationErrorEventArgs e) { try { var handler = GetValidationExceptionHandler(); var element = e.OriginalSource as UIElement; if (handler == null || element == null) return; if (e.Action == ValidationErrorEventAction.Added) { _validationExceptionCount++; } else if (e.Action == ValidationErrorEventAction.Removed) { _validationExceptionCount--; } handler.IsValid = _validationExceptionCount == 0; } catch (Exception ex) { throw ex; } } #endregion }
4. View容器最外层注册添加的监听错误事件 ValidationExceptionBehavior
<i:Interaction.Behaviors> <domain:ValidationExceptionBehavior></domain:ValidationExceptionBehavior> </i:Interaction.Behaviors>
5. ViewModel 通过实现 IValidationExceptionHandler 来获取前端的验证结果
根据前端验证的结果, 正确保存, 错误进行提示
public override void Save() { if (!this.IsValid) { MessageBox.Show("输入的格式有误,请重新输入!"); return; } base.Save(); }
效果: