Xamarin Model-View-ViewModel 模式

2025年3月17日 | 阅读 8 分钟

引言

MVVM 代表 Model-View-ViewModel。MVVM 代表将代码分为域相关数据(Model)、数据表示(View)以及程序逻辑(称为业务逻辑)(ViewModel)。ViewModel 的职责是可见性。

Xamarin.Forms 开发者需要使用 XAML 创建用户界面,然后添加代码以实现用户界面的操作。随着应用程序的修改和规模的增长,维护问题随之出现。这些问题包括 UI 控件与业务逻辑之间的紧密耦合,这增加了 UI 修改的成本,并且会使代码的单元测试变得困难。

Model-View-ViewModel (MVVM) 模式有助于将应用程序的业务和表示逻辑与其用户界面分离。在应用程序逻辑和 UI 之间保持清晰的分离有助于解决各种问题,并能使应用程序更容易测试、维护和发展。它还可以提高代码重用机会,并允许开发人员和 UI 设计师在开发应用程序各自部分时轻松协作。

MVVM 模式

MVVM 包含三个核心组件:Model、View 和 ViewModel。每个组件都具有不同的功能。

在这里,我们将展示三个组件之间不同的关系。

Xamarin Model-View-ViewModel Pattern

关系

此外,要理解每个组件的职责,了解它们如何相互作用也很重要。

从高层次看,View“了解”ViewModel,ViewModel 了解 Model,但 Model 不了解 ViewModel,ViewModel 也不了解 View。因此,ViewModel 将 View 与 Model 隔离开来,并允许 Model 独立于 View 进行演变。

MVVM 模式的优点

使用 MVVM 模式的优点是:

  • 如果存在一个现有模型实现,它加密了当前的业务逻辑,那么修改它可能会很困难或有风险。
  • 在这种情况下,ViewModel 充当模型类的适配器,使我们能够避免对模型代码进行任何重大更改。
  • 开发人员可以创建可视化模型和模型的单元测试,而无需使用 View。ViewModel 的单元测试可以利用与 View 相同的功能。
  • 只要 View 以 XAML 实现,应用程序的 UI 就可以在不触及代码的情况下进行重新设计。因此,新版本的 View 应该与现有 ViewModel 配合使用。
  • 设计师和开发人员可以在开发过程中独立且同步地处理各自的组件。
  • 设计师可以专注于可视化,而开发人员可以专注于可视化模型和模型组件。

视图 (View)

View 负责定义屏幕结构和外观的布局。每个 View 都以 XAML 定义;它包含有限的代码,不包括业务逻辑。在某些情况下,代码隐藏可能提供 UI 逻辑,实现难以在 XAML 中表达的视觉行为,例如动画。

在 Xamarin.Forms 应用程序中,View 是 Page 类型或 ContentView 派生类。View 也可以由数据模板呈现,数据模板指定了在显示对象时用于可视化对象的 UI 元素。数据模板作为 View,没有任何代码隐藏,它被设计为绑定到特定的 View Model 类型。有多种选项可以在 View Model 上执行代码以与 View 交互,例如按钮点击或项目选择。如果控件支持命令,控件的命令可以数据绑定到 View Model 上的 ICommand 属性。当控件的命令被调用时,View Model 中的代码将被执行。除了命令之外,行为可以附加到 View 中的对象,并且可以监听命令的调用或事件的触发。作为响应,行为可以调用 View Model 上的 ICommand 或 View Model 上的方法。

ViewModel

ViewModel 实现 View 可以绑定数据的属性和命令,并通过更改通知事件告知 View 任何状态更改。ViewModel 的命令和属性定义了 UI 的功能,但 View 决定了功能如何显示。

ViewModel 还负责协调 View 与任何所需模型类的交互。ViewModel 可能会选择将模型类直接暴露给 View,以便 View 中的控件可以直接将数据绑定到它们。在这种情况下,模型类需要设计为支持数据绑定和更改通知事件。每个 ViewModel 都以 View Model 易于消费的形式提供数据。ViewModel 有时会执行数据转换。将转换放在 ViewModel 中是一个好主意,因为它提供了 View 可以绑定的属性。例如,ViewModel 可能会结合两个属性的值,以便 View 更容易显示。

为了让 ViewModel 参与与 View 的双向数据绑定,其属性必须引发 PropertyChanged 事件。

ViewModel 通过实现 INotifyPropertyChanged 接口并在属性更改时扩展 PropertyChanged 事件来满足此要求。

对于集合,提供了 View 友好的 ObservableCollection<T>。此集合实现集合更改通知,使开发人员无需在集合上实现 INotifyCollectionChanged 接口。

模型

模型类是非可视类,封装了应用程序的数据。因此,模型可以被认为是代表应用程序的领域模型,通常包括数据模型以及业务和验证逻辑。

模型对象的示例包括数据传输对象 (DTO)、普通旧 CLR 对象 (POCO) 以及生成的实体和代理对象。模型类通常与服务或存储库结合使用,这些服务或存储库封装了数据访问和缓存。

连接 View Models 到 Views

ViewModel 可以使用 Xamarin.Forms 的数据绑定功能链接到 View。

有几种方法可以用于构建 View 和 ViewModel 并在运行时添加它们。

这些方法分为两类,称为View 优先组合和 ViewModel 优先组合。在 View 优先组合和 ViewModel 优先组合之间存在偏好和复杂性问题。但是,所有方法都具有相同的目标,即 View 的 BindingContext 属性分配有一个 ViewModel。

View 优先组合

这种方法的好处是它使得构建松散耦合、可单元测试的应用程序变得容易,因为 ViewModel 不依赖于 View 本身。通过遵循应用程序的视觉结构而不是跟踪代码的执行来了解类是如何创建和关联的,很容易理解应用程序的结构。此外,View 优先构建Xamarin.Forms 导航系统保持一致,该系统负责在导航发生时构建页面,这使得 ViewModel 优先组合复杂且与平台不符。

ViewModel 优先组合

ViewModel 优先组合对某些开发人员来说感觉更自然。因为 View 的创建可以抽象掉,从而使他们能够专注于应用程序的逻辑非 UI 结构。此外,它还可以使 ViewModel 由其他 ViewModel 创建。但是,这种方法通常很复杂,并且难以理解应用程序的各个部分是如何创建和关联的。

以下是连接 ViewModel 到 View 的方法。

声明式创建 ViewModel

View 最直接的方法是在 XAML 中声明式地初始化其对应的 ViewModel。当 View 被构造时,相应的 ViewModel 对象也将被构造。

我们将通过以下代码讨论此方法。

当 ContentPage 创建时,LoginViewModel 的实例会自动构造并设置为 View 的 BindingContext。

这种方法的优点是它很简单,但缺点是它要求 ViewModel 中有一个默认构造函数。

以编程方式创建 ViewModel

View 可以在代码隐藏文件中包含导致 ViewModel 被分配给其 BindingContext 属性的代码。这通常在 View 的构造函数中完成,如下面的代码所示

在 View 的代码隐藏中以编程方式构建和分配 ViewModel 的优点是它很简单。这种方法的缺点是 View 需要向 ViewModel 提供任何所需的依赖项。

创建定义为数据模板的 View

View 可以定义为数据模板,并与 ViewModel 类型关联。数据模板可以定义为资源,也可以内联定义在将显示 ViewModel 的控件中。控件的内容是 ViewModel 实例,数据模板用于在视觉上表示它。此技术是 ViewModel 首先初始化,然后创建 View 的情况的一个示例。

使用 ViewModel Locator 自动创建 ViewModel

ViewModel 定位器是一个自定义类,它管理 ViewModel 的初始化及其与 View 的关联。在 EShopOnContainers 移动应用程序中,ViewModelLocator 类具有一个附加属性 AutoWireViewModel,用于将 View 连接到 ViewModel。在 View 的 XAML 中,此附加属性设置为 true,表示 ViewModel 应自动连接到 View,如下面的代码所示

AutoWireViewModel 属性是一个可绑定属性,它被初始化为 false,当值更改时,会调用 OnAutoWireViewModelChanged 事件处理程序。此方法解析 View 的 ViewModel。

示例

OnAutoWireViewModelChanged 方法尝试使用基于约定的方法解析 ViewModel。此约定假定

  • ViewModel 与 View 类型位于同一程序集。
  • View 位于 -views 子命名空间中。
  • ViewModel 位于 -ViewModels 子命名空间中。
  • ViewModel 名称与 View 名称对应并以“ViewModel”结尾。

最后,OnAutoWireViewModelChanged 方法将 View 类型的 BindingContext 设置为解析的 ViewModel 类型,以确定有关 ViewModel 类型的更多信息。这种方法的优点是应用程序有一个负责 ViewModel 初始化及其与 View 连接的单一类。

总结

Model-View-ViewModel (MVVM) 模式有助于将应用程序的业务和表示逻辑与其用户界面 (UI) 分离。在应用程序逻辑和 UI 之间保持清晰的分离有助于解决众多开发问题,并能使应用程序更容易测试、维护和发展。它还可以提高代码重用机会,并允许开发人员和 UI 设计师在开发应用程序各自部分时轻松协作。

通过使用 MVVM 模式,应用程序的 UI、底层表示和业务逻辑被分为三个类。这些类是 View,它封装了表示逻辑和状态,以及 Model,它封装了业务逻辑和数据。


下一个主题Xamarin 面试题