WPF MVVM(Caliburn.Micro) 数据验证

书接前文

前文中仅是WPF验证中的一种,我们暂且称之为View端的验证(因为其验证规是写在Xaml文件中的)。

还有一种我们称之为Model端验证,Model通过继承IDataErrorInfo接口来实现,这个还没研究透,后面补上。

WPF MVVM Model端验证-待续

今天的主要内容是MVVM下的数据验证,主要使用View端验证,需求如下:

  • 1.对姓名的非空验证,验证错误控件后边应该有感叹号提示,感叹号的ToolTip应该有具体错误的信息
  • 2.对姓名的非空验证不通过的话,确定 按钮应该禁用

对于1,控件本身验证不通过会有一个红色的边框,后面的感叹号我们用Adorner来实现,且看这篇

WPF Adorner+附加属性 实现控件友好提示

不好处理的是2,为什么呢?在Mvvm中,我们故意分离View和VM,View只负责显示,VM负责各种交互逻辑,VM应该感知不到View的存在,而各种验证(不管你是VIew端验证还是Model端验证)产生的Validation.ErrorEvent冒泡事件只会沿着逻辑树上走,我们就是需要监听这个事件,有了这个事件我们的VM才能知道验证不通过,从而修改属性来达到禁用按钮的目的。也就是说View和VM之间除了传统的Binding和Command之外,还应该有一条通道,从View通知到VM的通道。

这里补充一下Validation.ErrorEvent,被验证的控件如下

   <TextBox Grid.Row="0"Grid.Column="1"Height="25"VerticalContentAlignment="Center"><TextBox.Text><Binding Path="IdentityName" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True"><Binding.ValidationRules><validationRules:RequiredRule ValidatesOnTargetUpdated="True"></validationRules:RequiredRule></Binding.ValidationRules></Binding></TextBox.Text></TextBox>

需要把NotifyOnValidationError设置为True才能够产生Validation.ErrorEvent事件,我们一般在所有要验证控件的最外层来注册监听这个事件

这是基本思路,实现的方式就很多,就看谁的优雅。最直接的就是在View的后台代码中直接注册监听这个事件,然后验证事件触发的时候,将
这个View的DataContext转成我们对应的ViewModel,就可以直接操作了。这样做也行,但是后面的项目基本会累死,因为这些都是体力活。
各种百度(百度基本没什么用),最后使用Bing搜索到一个老外写的代码
非常6,参考之后,决定改造一下。

先讲一下思路,继承WPF中的Behavior,取名ValidationExceptionBehavior,这个Behavior负责注册监听Validation.ErrorEvent事件,并且将验证结果通知到ViewModel
,要能够通用的话,必然ValidationExceptionBehavior不知道ViewModel的具体类型,于是我们设计了一个接口IValidationExceptionHandler,需要接受到来自view的验证结果
的ViewModel就需要实现这个接口。所以对于View,我们只需要在最外层容器加入下面一行代码

    <i:Interaction.Behaviors><behaviors:ValidationExceptionBehavior></behaviors:ValidationExceptionBehavior></i:Interaction.Behaviors>

对于ViewModel,我们只需要实现接口

[Export]
public class BaseInfoConfigViewModel : Screen, IValidationExceptionHandler
{public bool IsValid{get{return _isValid;}set{if (value == _isValid)return;_isValid = value;NotifyOfPropertyChange(() => IsValid);}}
}

该接口只有一个属性,就是IsValid,验证是否有效,通过这个属性,就可以在ViewModel中为所欲为了。好吧,讲这么多不如上代码,上Demo。

IValidationExceptionHandler.cs

/// <summary>
/// 验证异常处理接口,由VM来继承实现
/// </summary>
public interface IValidationExceptionHandler
{/// <summary>/// 是否有效/// </summary>bool IsValid{get;set;}
}

ValidationExceptionBehavior.cs

    /// <summary>
/// 验证行为类,可以获得附加到的对象
/// </summary>
public class ValidationExceptionBehavior : Behavior<FrameworkElement>
{#region 字段/// <summary>/// 错误计数器/// </summary>private int _validationExceptionCount = 0;private Dictionary<UIElement, NotifyAdorner> _adornerCache;#endregion#region 方法#region 重写方法/// <summary>/// 附加对象时/// </summary>protected override void OnAttached(){_adornerCache = new Dictionary<UIElement, NotifyAdorner>();//附加对象时,给对象增加一个监听验证错误事件的能力,注意该事件是冒泡的this.AssociatedObject.AddHandler(Validation.ErrorEvent, new EventHandler<ValidationErrorEventArgs>(this.OnValidationError));}#endregion#region 私有方法#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 显示Adorner/// <summary>/// 显示Adorner/// </summary>/// <param name="element"></param>/// <param name="errorMessage"></param>private void ShowAdorner(UIElement element, string errorMessage){NotifyAdorner adorner = null;//先去缓存找if (_adornerCache.ContainsKey(element)){adorner = _adornerCache[element];//找到了,修改提示信息adorner.ChangeToolTip(errorMessage);}//没有找到,那就New一个,加入到缓存else{adorner = new NotifyAdorner(element, errorMessage);_adornerCache.Add(element, adorner);}//将Adorner加入到if (adorner != null){var adornerLayer = AdornerLayer.GetAdornerLayer(element);adornerLayer.Add(adorner);}}#endregion#region 移除Adorner/// <summary>/// 移除Adorner/// </summary>/// <param name="element"></param>private void HideAdorner(UIElement element){//移除Adornerif (_adornerCache.ContainsKey(element)){var adorner = _adornerCache[element];var adornerLayer = AdornerLayer.GetAdornerLayer(element);adornerLayer.Remove(adorner);}}#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++;ShowAdorner(element, e.Error.ErrorContent.ToString());}else if (e.Action == ValidationErrorEventAction.Removed){_validationExceptionCount--;HideAdorner(element);}handler.IsValid = _validationExceptionCount == 0;}catch (Exception ex){throw ex;}}#endregion#endregion#endregion}

NotifyAdorner.cs

    /// <summary>
/// 提示Adorner
/// </summary>
public class NotifyAdorner : Adorner
{private VisualCollection _visuals;private Canvas _canvas;private Image _image;private TextBlock _toolTip;/// <summary>/// 构造/// </summary>/// <param name="adornedElement"></param>/// <param name="errorMessage"></param>public NotifyAdorner(UIElement adornedElement, string errorMessage) : base(adornedElement){_visuals = new VisualCollection(this);BuildNotifyStyle(errorMessage);_canvas = new Canvas();_canvas.Children.Add(_image);_visuals.Add(_canvas);}private void BuildNotifyStyle(string errorMessage){_image = new Image(){Width = 20,Height = 20,Source = new BitmapImage(new Uri("你的图片路径", UriKind.Absolute))};_toolTip = new TextBlock() { FontSize = 14, Text = errorMessage };_image.ToolTip = _toolTip;}protected override int VisualChildrenCount{get{return _visuals.Count;}}protected override Visual GetVisualChild(int index){return _visuals[index];}public void ChangeToolTip(string errorMessage){_toolTip.Text = errorMessage;}protected override Size MeasureOverride(Size constraint){return base.MeasureOverride(constraint);}protected override Size ArrangeOverride(Size finalSize){_canvas.Arrange(new Rect(finalSize));_image.Margin = new Thickness(finalSize.Width + 2, 0, 0, 0);return base.ArrangeOverride(finalSize);}
}

当然,如有错误,请大家斧正。

转载于:https://www.cnblogs.com/HelloMyWorld/p/5438354.html

WPF MVVM 验证相关推荐

  1. WPF MVVM从入门到精通1:MVVM模式简介

    WPF MVVM从入门到精通1:MVVM模式简介 原文:WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通2:实现一个登录 ...

  2. 疯狂 java轻量级框架_ViewModel从未如此清爽 - 轻量级WPF MVVM框架Stylet

    Stylet是我最近发现的一个WPF MVVM框架, 在博客园上搜了一下, 相关的文章基本没有, 所以写了这个入门的文章推荐给大家. Stylet是受Caliburn Micro项目的启发, 所以借鉴 ...

  3. WPF自学入门(十一)WPF MVVM模式Command命令 WPF自学入门(十)WPF MVVM简单介绍...

    WPF自学入门(十一)WPF MVVM模式Command命令 在WPF自学入门(十)WPF MVVM简单介绍中的示例似乎运行起来没有什么问题,也可以进行更新.但是这并不是我们使用MVVM的正确方式.正 ...

  4. (WPF, MVVM) Event 处理

    原文:(WPF, MVVM) Event 处理 WPF的有些UI元素有Command属性可以直接实现绑定,如Button 但是很多Event的触发如何绑定到ViewModel中的Command呢? 答 ...

  5. wpf mvvm模式下CommandParameter传递多参

    wpf mvvm模式下CommandParameter传递多参 原文:wpf mvvm模式下CommandParameter传递多参 CommandParameter一般只允许设置一次,所以如果要传递 ...

  6. WPF MVVM实例三

    在没给大家讲解wpf mwm示例之前先给大家简单说下MVVM理论知识: WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时 ...

  7. C# WPF MVVM模式Prism框架下事件发布与订阅

    01 - 前言 处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Prism提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间通过事 ...

  8. C# WPF MVVM模式Prism框架从零搭建(经典)

    01 - 前言 目前最新的PRISM的版本是8.1.97,本节以6.3.0.0 讲解,可以在Github上获取PRISM的源码. Prism Github地址:https://github.com/P ...

  9. C# WPF MVVM项目实战(进阶②)

    这篇文章还是在之前用Caliburn.Micro搭建好的框架上继续做的开发,今天主要是增加了一个用户窗体ImageProcessView,然后通过Treeview切换选择项之后在界面显示不同效果的图片 ...

最新文章

  1. ab测试nginx Nginx性能优化
  2. C++ Primer 读书笔记 - 第十三章
  3. java urlstreamhandler_获取对Java的默认http(s)URLStreamHandler的引用
  4. Ajax系列之JSON数据格式
  5. 【BZOJ-2325】道馆之战 树链剖分 + 线段树
  6. 学习笔记26_MVC前台强类型参数
  7. jit编译_意外分配– JIT编译抖动
  8. linux安装源码mysql失败,linux停mysql源码安装
  9. 服务器系统怎么找便签,Win10电脑怎么找回便签记录?如何恢复误删的内容?
  10. ActiveX 控件重绘无效问题,用CClientDC 而不是CPaintDC
  11. 华为p8刷linux系统,华为手机助手ROM一键刷机
  12. 微带贴片天线-微带线馈电
  13. 集线器(HUB)、交换机(Switch)、路由器(Router)
  14. ZYT and LBC
  15. C语言编写程序求1到100的和,C语言菜鸟基础教程之求1到100的和
  16. ipad端网页屏幕变小了
  17. 干货 | 关于SwiftUI,看这一篇就够了
  18. 【前端】性能优化 - WebP
  19. 史无前例的这个 GitHub 汇总了 300 道 Python 面试题
  20. 多家银行ATM机取款手续费比较-文图

热门文章

  1. AgreementMaker:Efficient Matching for Large Real-World 翻译
  2. View 绘制体系知识梳理(4) 绘制过程之 Layout 详解
  3. grafana高可用架构
  4. ABI (应用程序二进制接口)
  5. An unknown error occurred.
  6. 如何在windows 2008 server 新建用户
  7. windows消息宏(转)
  8. Zabbix 神器——自动发现
  9. 在32位windows 7下安装PyLucene
  10. 完成端口与高性能服务器程序开发