WPF MVVM 验证
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 验证相关推荐
- WPF MVVM从入门到精通1:MVVM模式简介
WPF MVVM从入门到精通1:MVVM模式简介 原文:WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通2:实现一个登录 ...
- 疯狂 java轻量级框架_ViewModel从未如此清爽 - 轻量级WPF MVVM框架Stylet
Stylet是我最近发现的一个WPF MVVM框架, 在博客园上搜了一下, 相关的文章基本没有, 所以写了这个入门的文章推荐给大家. Stylet是受Caliburn Micro项目的启发, 所以借鉴 ...
- WPF自学入门(十一)WPF MVVM模式Command命令 WPF自学入门(十)WPF MVVM简单介绍...
WPF自学入门(十一)WPF MVVM模式Command命令 在WPF自学入门(十)WPF MVVM简单介绍中的示例似乎运行起来没有什么问题,也可以进行更新.但是这并不是我们使用MVVM的正确方式.正 ...
- (WPF, MVVM) Event 处理
原文:(WPF, MVVM) Event 处理 WPF的有些UI元素有Command属性可以直接实现绑定,如Button 但是很多Event的触发如何绑定到ViewModel中的Command呢? 答 ...
- wpf mvvm模式下CommandParameter传递多参
wpf mvvm模式下CommandParameter传递多参 原文:wpf mvvm模式下CommandParameter传递多参 CommandParameter一般只允许设置一次,所以如果要传递 ...
- WPF MVVM实例三
在没给大家讲解wpf mwm示例之前先给大家简单说下MVVM理论知识: WPF技术的主要特点是数据驱动UI,所以在使用WPF技术开发的过程中是以数据为核心的,WPF提供了数据绑定机制,当数据发生变化时 ...
- C# WPF MVVM模式Prism框架下事件发布与订阅
01 - 前言 处理同模块不同窗体之间的通信和不同模块之间不同窗体的通信,Prism提供了一种事件机制,可以在应用程序中低耦合的模块之间进行通信,该机制基于事件聚合器服务,允许发布者和订阅者之间通过事 ...
- C# WPF MVVM模式Prism框架从零搭建(经典)
01 - 前言 目前最新的PRISM的版本是8.1.97,本节以6.3.0.0 讲解,可以在Github上获取PRISM的源码. Prism Github地址:https://github.com/P ...
- C# WPF MVVM项目实战(进阶②)
这篇文章还是在之前用Caliburn.Micro搭建好的框架上继续做的开发,今天主要是增加了一个用户窗体ImageProcessView,然后通过Treeview切换选择项之后在界面显示不同效果的图片 ...
最新文章
- ab测试nginx Nginx性能优化
- C++ Primer 读书笔记 - 第十三章
- java urlstreamhandler_获取对Java的默认http(s)URLStreamHandler的引用
- Ajax系列之JSON数据格式
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
- 学习笔记26_MVC前台强类型参数
- jit编译_意外分配– JIT编译抖动
- linux安装源码mysql失败,linux停mysql源码安装
- 服务器系统怎么找便签,Win10电脑怎么找回便签记录?如何恢复误删的内容?
- ActiveX 控件重绘无效问题,用CClientDC 而不是CPaintDC
- 华为p8刷linux系统,华为手机助手ROM一键刷机
- 微带贴片天线-微带线馈电
- 集线器(HUB)、交换机(Switch)、路由器(Router)
- ZYT and LBC
- C语言编写程序求1到100的和,C语言菜鸟基础教程之求1到100的和
- ipad端网页屏幕变小了
- 干货 | 关于SwiftUI,看这一篇就够了
- 【前端】性能优化 - WebP
- 史无前例的这个 GitHub 汇总了 300 道 Python 面试题
- 多家银行ATM机取款手续费比较-文图