IValueConverter,做 WPF 的都应该接触过,把值换成 Visibility 、Margin 等等是最常见的例子,也有很多很好的博文解释过用法。本文只是解释一下,MVVM 中一些情景。

我遇到过一个用例,做些简单的数据可视化。要求把 enum 换成图片。

MVVM 模式下,是通过 ViewModel 把业务类 Model 暴露给 View,用绑定完成 ViewModel 和 View 的连接。这样的话,Model 内的 enum 直接绑到 View 时候,视图显示了 enum 值 ToString() 的结果。

用个真实例子说明。为代码更简单,以下没有用任何其他第三方控件或类库。

看看 Model 是怎样的:

using System.ComponentModel;namespace Lepton_Practical_MVVM_4 {public class MyModel : INotifyPropertyChanged {#region Domain Properties private long _ID;///<summary>/// DB Key///</summary>public virtual long ID {get {return _ID;}set {_ID = value;if (PropertyChanged !=null)PropertyChanged(this, new PropertyChangedEventArgs("ID"));}}private MyStatusEnum _Status;///<summary>/// Document Status///</summary>public virtual MyStatusEnum Status {get {return _Status;}set {_Status = value;if (PropertyChanged !=null)PropertyChanged(this, new PropertyChangedEventArgs("Status"));}}private string _DocNo;///<summary>/// Document number///</summary>public virtual string DocNo {get {return _DocNo;}set {_DocNo = value;if (PropertyChanged !=null)PropertyChanged(this, new PropertyChangedEventArgs("DocNo"));}}#endregion

     #region INotifyPropertyChanged Members

     public virtual event PropertyChangedEventHandler PropertyChanged;#endregion}
}

MyStatusEnum:

namespace Lepton_Practical_MVVM_4 {public enum MyStatusEnum {OK =0,Cancel =1,Delete =2}
}

这个业务类,将使用 ORM, NHibernate 做 persistence,这个样子加上 XXX.hbm.xml 映射能直接调用 session.Save() 来储存的了。enum 会自动储存为 int。数据层不写出来了。使用其他 ORM 也大同小异。

但直接绑定后,这样子不太 User-Friendly,也不够华丽。

这用例是做可视化,把状态换成图片。

要绑定图片,绑定的话,最直观的想法,改 Model。Model 内有图片Source 属性,好像就什么都解决了。

using System.ComponentModel;
using System.Windows.Media;
using System;
using System.Windows.Media.Imaging;namespace Lepton_Practical_MVVM_4 {publicclass MyModel : INotifyPropertyChanged {#region Domain Properties private long _ID;///<summary>/// DB Key///</summary>

     public virtual long ID {get {return _ID;}set {_ID = value;if (PropertyChanged !=null)PropertyChanged(this, new PropertyChangedEventArgs("ID"));}}private MyStatusEnum _Status;///<summary>     /// Status///</summary>     public virtual MyStatusEnum Status {
get {return _Status;}set {_Status = value;if (PropertyChanged !=null)PropertyChanged(this, new PropertyChangedEventArgs("Status"));}}private string _DocNo;///<summary>     /// Document Number///</summary>     public virtual string DocNo {
get {return _DocNo;}set {_DocNo = value;if (PropertyChanged !=null)PropertyChanged(this, new PropertyChangedEventArgs("DocNo"));}}#endregion     public virtual ImageSource StatusImage {get {switch (Status) {case MyStatusEnum.OK:return new BitmapImage(new Uri("pack://application:,,,/Img/Check.png"));case MyStatusEnum.Cancel:return new BitmapImage(new Uri("pack://application:,,,/Img/Cancel.png"));case MyStatusEnum.Delete:return new BitmapImage(new Uri("pack://application:,,,/Img/Delete.png"));default:throw new InvalidEnumArgumentException("MyStatusEnum out of expected range");}}}#region INotifyPropertyChanged Members     public virtual event PropertyChangedEventHandler PropertyChanged;#endregion}
}

XAML:

<Window x:Class="Lepton_Practical_MVVM_4.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Lepton Practical MVVM 4 IConverter"Height="350" Width="525"><Grid><ListView ItemsSource="{Binding Path=MyItems}"><ListView.View><GridView><GridViewColumn DisplayMemberBinding="{Binding Path=Status}" Header="状态" Width="50"/><GridViewColumn DisplayMemberBinding="{Binding Path=DocNo}" Header="单号" Width="100"/><GridViewColumn Header="图片"><GridViewColumn.CellTemplate><DataTemplate><Image Source="{Binding Path=StatusImage}" Width="20" Height="20"/></DataTemplate></GridViewColumn.CellTemplate></GridViewColumn></GridView></ListView.View></ListView></Grid></Window>

效果:

StatusImage 这个字段,不需要映射不用保存到数据库,貌似没问题。

但一旦运行时 Status 值变化,问题就出来了。

运行时,MyStatusEnum 值如果动态改,你会发现,状态栏的值会变,图片却不变。问题原因很明显,值变化发生在 Status,由它发出 PropertyChanged 通知,通知 Status 变了,不是 StatusImage变。点击这里下载这版本的代码。

然后又想办法,或许在 Status 变动时同时发Status 和 StatusImage 两个变化通知?又或许 PropertyChanged(null) 全部刷新?这不是某某方式能不能做到的问题,开发嘛,再乱再差代码,有时候也能做到用例要求。

以上是真实项目中我见过的修改过程,这做法当然被推翻,原因如下:

  1. Model 层为了 View 层的显示,添加了代码
    Model 类,为显示方式而改动(添加了一个不做映射的只读属性),是设计上的错误。图片选择的代码也在 Model 内(见 StatusImage 属性 get 内的 switch),也是设计错误。
  2. 图片与 Model 捆绑了在一起
    如果改图片,需要改 Model 属性的 switch 内的 URI。万一,我要保留这图片这显示方式在这视图,但在另一个界面显示另一张图,比如我有另外一个视图需要把 Delete 和 Cancel 显示为同一张图,判断就不是那么简单。可能需要用到视图的引用,或者格外加参数、属性等等来判断显示哪个。

正确做法,是用 IValueConverter 做转换,IValueConverter 是显示层(View)的。需要不同逻辑的话,每个界面用自己的 Converter,逻辑独立,图片只跟 View 捆绑,ViewModel 和 Model 不需理会。

IValueConverter 写法很简单,在其他博文已经多次介绍。这里不重复了。用它的时候,绑定的是业务数据的字段,当它变化,图片自然会更新了。点击这里下载使用了 IValueConverter 的完整代码。

使用 MVVM 模式,一旦牵涉到改 Model 或 ViewModel,请三思。只为显示的话,几乎可以马上肯定,不该做在 Model 和 ViewModel。反过来,业务逻辑出现在 View 也是不正确。破坏了设计,自然享受不了良好设计的优势。

我在这群里,欢迎加入交流:
开发板玩家群 578649319
硬件创客 (10105555)

转载于:https://www.cnblogs.com/leptonation/p/3291581.html

C# WPF MVVM 实战 – 4 - 善用 IValueConverter相关推荐

  1. C# WPF MVVM 实战 – 3 – 树结构

    树结构放在 WPF ,有大家熟悉的 TreeView.Menu / MenuItem 等等,自定义的话它是 HierarchicalDataTemplate. 用上 MVVM 模式,视图与数据分离,意 ...

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

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

  3. C# 值得永久收藏的WPF项目实战(经典)

    01 - 简介 之前也写过好多篇CM框架相关的项目实战文章,比如: C# WPF框架Caliburn.Micro快速搭建 C# WPF框架Caliburn.Micro入门实例1 C# WPF MVVM ...

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

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

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

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

  6. WPF MVVM 弹框之等待框

    WPF MVVM 弹框之等待框 目录 一.效果 二.弹框主体改造 三.等待动画用户控件 四.弹窗 ViewModel 和帮助类的改造 五.使用方法和代码地址 独立观察员 2020年10月13日 之前写 ...

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

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

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

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

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

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

  10. (WPF, MVVM) Event 处理

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

最新文章

  1. 「AI初识境」为了围剿SGD大家这些年想过的那十几招
  2. flex 3.0序列号
  3. javascript工具类(util)-持续更新
  4. 【django】解决关于RuntimeError: ‘cryptography‘ package is required for sha256_password or caching...问题
  5. Python 数据分析三剑客之 Pandas(十):数据读写
  6. SQL 必知必会·笔记7汇总数据——使用聚合函数
  7. 闲来无事,画个佩奇可好?
  8. dosbox中out of memory_flink教程-详解flink 1.11 中的JDBC Catalog
  9. 网页采集器,全自动网站采集发布(图文)
  10. 如何用pdfFactory新建打印机并设置属性
  11. 计算机软件系统故障的分类,系统故障
  12. ios逆向工具theos安装和使用tweak替换和卸载
  13. android 打开wifi并链接到制定ip,在安卓WiFi-Direct连接中获取对等设备IP地址的方法及设备与流程...
  14. vray许可服务器信息怎么看不到,VRay for sketchup的许可证问题怎么解决?
  15. 计算机科学与技术专业每年毕业人数,毕业生人数最多的10个本科专业:计算机科学与技术...
  16. 潮汕古韵之都,这次目的地——潮州
  17. 解读PMP考点:管理储备和应急储备
  18. 16进制与byte的转换
  19. PHP中常见的设计模式
  20. c语言扩散,可扩散列--可扩散列头文件C语言

热门文章

  1. 台北故宫博物院收藏:气势开张,米芾行草书法真迹《真酥帖》赏析
  2. 微信小程序踩坑——项目内文件夹删除不掉
  3. Mysql Workbench 8,连接时显示An AppArmor policy prevents this sender from sending this message to this rec
  4. Dota2 on Ubuntu
  5. 空间可分离卷积和深度可分离卷积
  6. 数字图像处理与Python实现笔记之图像压缩
  7. java发展观_科学发展观的第一要义是以人为本。
  8. KVM之父的新作ScyllaDB:用C++开发的Cassandra兼容列数据库,性能提升10倍
  9. 养猫有哪些坏处哪些好处呢?
  10. java:/comp/env_启动日志中就出现[java:comp/env/spring.liveBeansView.mbeanDomain] not found这个日志...