C# WPF MVVM 实战 – 2.2

上一篇,开始了采购订单的开发,谈完 Combo Box 怎样绑定到 ViewModel 的集合,还把选定项的其他信息一并显示在 View 的另一个控件(TextBlock)。这次继续谈表头的部分,和介绍明细行的绑定。

由于单纯把单一个普通控件绑到 ViewModel 属性比较简单,我只介绍一个,其他的请自行看代码。

VIEWMODELS

就拿单据号来看看。它在 View 上是一个 TextBox,在 Model 内是 string,没有比这更简单的了。以下是 View 的 XAML:

image

然后是 ViewModel 的相关代码:

image

代码是接着上一篇来写下去的。上次的部分我收了起来,需要的请看上一篇

这里实在没有什么好说的,唯一的是,我在 get 和 set 都直接读取或写入业务类实例的属性内,这免去了一层映射。不喜欢把 Model 类直接暴露到 View 的,不喜欢我这方式的,就只能在 ViewModel 多保存一个private/internal string,在保存或更新时候你自己映射到 Model 实例。绝大多数情况中,我认为没有这必要这样分隔。

然后,我直接谈谈明细行了。它有好几个有趣的地方。

image

首先是添加、删除行按钮。其实很多第三方控件,都已经有这些功能,而且实现得比我这样写好很多,比如,右击带出菜单来删行,最后一行有个添加行按钮等等。要我自己弄到那样要花点功夫,而且跑题了。就先这样吧。

说 WPF 一切都绑定。是的,连命令也可以绑定。看看这两个按钮的 XAML:

image

如果你过往用 Win Form,现在用 WPF 而且正在学习 MVVM,最好是把 WinForm 的一切忘掉。Button 的命令不是双击设计界面生成句柄来做的,是上面这做法。Command 绑定,要求是绑到实现了 ICommand 接口的实例,在我第一篇已经提到过 RelayCommand,你可以用它,或者你喜欢的用别的也行,比如 DelegateCommand。

按照上面 XAML 的写法,意思是 DataContext,即 ViewModel 内,有两个 public 属性 (AddRowCommand / DeleteRowCommand),它们都是实现了 ICommand 接口的。那么,看看 ViewModel 代码,先看看 AddRowCommand,用来添加新明细行的:

image

有人不喜欢我这种写法,他们喜欢在 ViewModel 的构造函数,初始化时候,把 addRowCommand 通通创建实例先,get 内直接返回 addRowCommand。我没觉得有什么明显分别,你喜欢。

由于上一篇的代码内,已经写了,PoDetails 是 ObserverableCollection<T>,所以当你用代码添加行时候,视图会收到通知,就会更新显示。单纯用 List 之类来做 PoDetails 集合,或者其他没有实现 INotifyCollectionChanged 的集合,是做不到这效果的。

第一篇也已经提到,RelayCommand 的设计,当构造函数是一个参数时候,CanExecute 永远返回 True,这命令是永远都能执行,Button 永远都是 enable 的。要 disable 的时候,做法可以看看我 ViewModel 的另一个 ICommand 属性,DeleteRowCommand:

image

当我没有点击任何行,没有选任何行,我希望“删除行”按钮被禁用,disable,灰色。要做到这效果,我需要知道用户有没有选择了 GridView 内的行。所以我加了一个属性来记录当前选定的行,是哪个,它的类型,当然是行记录业务对象 PurchaseOrderDetail:

image

 

只要把 GridView 的选中行,绑到 CurrentRow,然后让deleteRowCommand 内 CanExecute 委托(构造函数第二个参数)来检查 CurrentRow 是否为空 null,就能做到了。GridView 的选中行,绑到 CurrentRow 的 XAML 代码如下:

image

另外有了 CurrentRow,删除行时候视图触发命令时候,也不需要参数,不用告诉 ViewModel 当前行是哪个,因为当前行就是 CurrentRow。要删除它,就像 ViewModel 的 DeleteRow() 第 130 行代码那样,直接 remove() 它即可。

由于 CurrentRow 为 null 时候(用户没有选任何行的时候),deleteRowCommand 的 CanExecute 会返回 False,这命令是无法执行的,界面按钮是 disable 状态,所以在 DeleteRow 连检查 CurrentRow 是否为空等的代码也可以省掉了。代码能运行到 DeleteRow() 时候,CurrentRow 必定有值,remove 代码不会抛异常。

说了半天,给大家看看效果图:

没有任何行的时候

image

添加了行,但 GridView 没有焦点,没选行的时候

image

添加了行,选中了的时候

image

删除了行,没有再次选中行的时候

 

 

 

 

image

 

ViewModel 代码的文本,我下一次才贴出来,那样比较完整。

接下去,下一次,会讲物料号的绑定。它的特别之处,在于物料号是选项,选项列表来自系统的 Inventory 集合,这集合,是所有明细行共用的,不是一行一个新集合。MVVM 模式下,怎样能做到?下回介绍

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

posted @ 2012-05-10 01:38  Lepton  阅读(3394)  评论(5编辑  收藏  举报