“第 18 章: MVVM
注意
本书于 2016 年春季出版,之后再未更新。 书中有许多内容仍然有价值,但有些内容已过时,有些主题不再完全正确或完整。
架构应用程序的最佳方式之一是,将用户界面与基础代码(有时称为“业务逻辑”)分离开来。 虽然有多种技术,但为基于 XAML 的环境定制的技术称为“模型-视图-视图模型 (MVVM)”。
MVVM 相互关系
MVVM 应用程序有三层:
- 模型:提供基础数据(有时通过文件或 Web 访问来提供)
- 视图:用户界面或表示层,通常在 XAML 中实现
- 视图模型:连接模型和视图
模型对视图模型未知,视图模型对视图未知。 这三层通常使用以下机制相互连接:
在许多较小的程序(甚至更大的程序)中,模型通常是不存在的,或它的功能集成到视图模型中。
视图模型和数据绑定
视图模型必须能够在视图模型的属性更改时通知视图,才能进行数据绑定。 为此,视图模型在 System.ComponentModel
命名空间中实现 INotifyPropertyChanged
接口。 这是属于 .NET(而不是 Xamarin.Forms)的范畴。 (视图模型通常会尝试维持平台独立性。)
INotifyPropertyChanged
接口声明一个名为 PropertyChanged
的事件,此事件指明属性已更改。
视图模型时钟
Xamarin.FormsBook.Toolkit 库中的 DateTimeViewModel
定义了类型为 DateTime
的属性,此属性以计时器为依据更改。 此类实现 INotifyPropertyChanged
,每当 DateTime
属性更改时都会触发 PropertyChanged
事件。
MvvmClock 示例实例化此视图模型,并使用与视图模型的数据绑定来显示更新后的日期和时间信息。
视图模型中的交互式属性
视图模型中的属性可以更具交互性,如 SimpleMultiplier 示例中的 SimpleMultiplierViewModel
类所示。 数据绑定从两个 Slider
元素提供被乘数和乘数值,并使用 Label
显示产品。 不过,可以在 XAML 中对此用户界面进行大量更改,而不需要对视图模型或代码隐藏文件进行后续更改。
颜色视图模型
Xamarin.FormsBook.Toolkit 库中的 ColorViewModel
集成了 RGB 和 HSL 颜色模型。 如 HslSliders 示例所示:
简化视图模型
可以使用自动获取调用属性名称的 CallerMemberName
特性来定义 OnPropertyChanged
方法,从而简化视图模型中的代码。 Xamarin.FormsBook.Toolkit 库中的 ViewModelBase
类就是这样做的,并为视图模型提供了基类。
命令接口
MVVM 与数据绑定结合使用,数据绑定又与属性结合使用,因此 MVVM 似乎不足以处理 Button
的 Clicked
事件或 TapGestureRecognizer
的 Tapped
事件。 为允许视图模型处理此类事件,Xamarin.Forms 支持命令接口。
命令接口在包含两个公共属性的 Button
中显示:
ICommand
类型的Command
(在System.Windows.Input
命名空间中定义)Object
类型的CommandParameter
为了支持命令接口,视图模型必须定义类型为 ICommand
的属性,此属性随后数据绑定到 Button
的 Command
属性。 ICommand
接口声明了两种方法和一个事件:
- 包含
object
类型参数的Execute
方法 - 包含返回
bool
的object
类型参数的CanExecute
方法 CanExecuteChanged
事件
在内部,视图模型将类型为 ICommand
的每个属性设置为实现 ICommand
接口的类实例。 通过数据绑定,Button
最初调用 CanExecute
方法,并在此方法返回 false
时禁用自身。 它还设置 CanExecuteChanged
事件处理程序,并在相应事件触发时调用 CanExecute
。 如果 Button
已启用,它在用户单击 Button
时调用 Execute
方法。
你可能有一些早于 Xamarin.Forms 的视图模型,这些视图模型可能已支持命令接口。 对于旨在仅与 Xamarin.Forms 配合使用的新视图模型,Xamarin.Forms 提供了实现 ICommand
接口的 Command
类和 Command<T>
类。 泛型类型是 Execute
和 CanExecute
方法的参数类型。
简单方法执行
PowersOfThree 示例展示了如何在视图模型中使用命令接口。 PowersViewModel
类定义了两个类型为 ICommand
的属性,以及它传递给最简单 Command
构造函数的两个私有属性。 此程序包含从这一视图模型到两个 Button
元素的 Command
属性的数据绑定。
在 XAML 中,可以将 Button
元素轻松替换为 TapGestureRecognizer
对象,无需更改任何代码。
接近真实的计算器
AddingMachine 示例同时使用 ICommand
的 Execute
和 CanExecute
方法。 它使用 Xamarin.FormsBook.Toolkit 库中的 AdderViewModel
类。 视图模型包含六个类型为 ICommand
的属性。 这些是从 Command
的 Command
构造函数和 Command
构造函数,以及 Command<T>
的 Command<T>
构造函数初始化。 添加计算机的数字键全部绑定到使用 Command<T>
初始化的属性,Execute
和 CanExecute
的 string
参数标识特定键。
视图模型和应用程序生命周期
AddingMachine 示例中使用的 AdderViewModel
还定义了两个名为 SaveState
和 RestoreState
的方法。 这两种方法分别在应用程序休眠和再次启动时从应用程序中调用。