WPF的MVVM模式核心架构与实现细节

MVVM 模式核心架构与实现细节

    • [1. MVVM 模式回顾](#1. MVVM 模式回顾)
    • [2. ViewModel 的职责与设计](#2. ViewModel 的职责与设计)
      • [2.1 状态管理](#2.1 状态管理)
      • [2.2 行为暴露](#2.2 行为暴露)
    • [3. View 与 ViewModel 的连接](#3. View 与 ViewModel 的连接)
      • [3.1 DataContext 的设置](#3.1 DataContext 的设置)
      • [3.2 ViewModelLocator 模式](#3.2 ViewModelLocator 模式)
    • [4. Model 的角色与实现](#4. Model 的角色与实现)
    • [5. 服务层与依赖注入](#5. 服务层与依赖注入)
      • [5.1 服务层抽象](#5.1 服务层抽象)
      • [5.2 使用依赖注入容器](#5.2 使用依赖注入容器)
    • [6. 综合实战:构建一个完整的 MVVM 应用](#6. 综合实战:构建一个完整的 MVVM 应用)
    • [7. 总结](#7. 总结)

在前面的章节中,我们已经分别探讨了INotifyPropertyChangedICommand,它们是 MVVM(Model-View-ViewModel)模式的技术基石。本章将从更高层面整合这些知识,深入解析 MVVM 模式的核心架构思想,并展示如何构建一个结构清晰、可维护、可测试的 WPF 应用程序。

1. MVVM 模式回顾

MVVM 模式旨在将用户界面(View)的开发与业务逻辑和数据(Model)的开发分离,通过一个名为 ViewModel 的中间层来连接它们。

  • Model:负责应用程序的数据和业务逻辑。它不关心 UI,纯粹是业务领域的抽象。
  • View:负责界面的显示和用户的交互。它通过数据绑定与 ViewModel 交互,不包含任何业务逻辑。
  • ViewModel :作为 View 和 Model 之间的桥梁,它负责从 Model 中获取数据,并将其转换为 View 可以显示的格式。同时,它也暴露命令(ICommand)来响应 View 中的用户交互。

这种分离带来了诸多好处:

  • 可测试性:ViewModel 是纯 C#类,可以独立于 UI 进行单元测试。
  • 可维护性:UI 设计和业务逻辑开发可以并行进行,代码职责清晰。
  • 代码复用:ViewModel 可以在不同的 View 中复用。

2. ViewModel 的职责与设计

ViewModel 是 MVVM 模式的核心,其主要职责是状态管理和行为暴露。

2.1 状态管理

ViewModel 负责维护 View 所需的所有状态,并通过INotifyPropertyChanged通知 View 更新。

ViewModel 状态示例:

csharp 复制代码
public class UserProfileViewModel : ViewModelBase
{
    private string _userName;
    public string UserName
    {
        get => _userName;
        set => SetField(ref _userName, value);
    }

    private bool _isLoading;
    public bool IsLoading
    {
        get => _isLoading;
        set => SetField(ref _isLoading, value);
    }
}

2.2 行为暴露

ViewModel 通过ICommand接口向 View 暴露行为,而不是事件。

ViewModel 命令示例:

csharp 复制代码
public class UserProfileViewModel : ViewModelBase
{
    public ICommand SaveCommand { get; }
    public ICommand LoadProfileCommand { get; }

    public UserProfileViewModel()
    {
        SaveCommand = new RelayCommand(ExecuteSave, CanSave);
        LoadProfileCommand = new AsyncCommand(ExecuteLoadProfile);
    }

    private async Task ExecuteLoadProfile()
    {
        IsLoading = true;
        try
        {
            // 从服务加载数据
            var user = await _userService.GetUserAsync(CurrentUserId);
            UserName = user.Name;
        }
        finally
        {
            IsLoading = false;
        }
    }

    private void ExecuteSave()
    {
        // 保存逻辑
    }

    private bool CanSave()
    {
        return !string.IsNullOrWhiteSpace(UserName) && !IsLoading;
    }
}

3. View 与 ViewModel 的连接

View 通过DataContext属性与 ViewModel 建立连接。

3.1 DataContext 的设置

最直接的方式是在 View 的后台代码中设置:

csharp 复制代码
public partial class UserProfileView : Window
{
    public UserProfileView()
    {
        InitializeComponent();
        this.DataContext = new UserProfileViewModel();
    }
}

3.2 ViewModelLocator 模式

在大型应用中,为了更好地解耦和管理 ViewModel 的生命周期,通常采用 ViewModelLocator 模式,并结合依赖注入(DI)容器。

ViewModelLocator 示例:

csharp 复制代码
public class ViewModelLocator
{
    public IServiceProvider Services { get; }

    public ViewModelLocator()
    {
        var services = new ServiceCollection();

        // 注册服务
        services.AddSingleton<IUserService, MockUserService>();

        // 注册ViewModel
        services.AddTransient<UserProfileViewModel>();

        Services = services.BuildServiceProvider();
    }

    public UserProfileViewModel UserProfile => Services.GetRequiredService<UserProfileViewModel>();
}

App.xaml中声明 Locator:

xml 复制代码
<Application.Resources>
    <local:ViewModelLocator x:Key="Locator" />
</Application.Resources>

在 View 中设置DataContext

xml 复制代码
<Window ...
        DataContext="{Binding Source={StaticResource Locator}, Path=UserProfile}">
</Window>

4. Model 的角色与实现

Model 是应用程序的领域模型,通常是简单的 POCO(Plain Old CLR Object)对象。

Model 示例:

csharp 复制代码
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

在某些需要双向绑定的场景中,Model 也可以实现INotifyPropertyChanged接口。

5. 服务层与依赖注入

为了保持 ViewModel 的纯粹性,所有与外部世界的交互(如数据库访问、API 调用)都应该抽象到服务层中。

5.1 服务层抽象

csharp 复制代码
public interface IUserService
{
    Task<User> GetUserAsync(int userId);
    Task SaveUserAsync(User user);
}

public class UserService : IUserService
{
    // 实现具体的数据库或API调用逻辑
}

5.2 使用依赖注入容器

通过构造函数将服务注入到 ViewModel 中,这使得 ViewModel 更易于测试。

csharp 复制代码
public class UserProfileViewModel : ViewModelBase
{
    private readonly IUserService _userService;

    public UserProfileViewModel(IUserService userService)
    {
        _userService = userService ?? throw new ArgumentNullException(nameof(userService));
        // ...
    }
}

6. 综合实战:构建一个完整的 MVVM 应用

结合以上概念,一个典型的 WPF MVVM 应用结构如下:

  1. Views/:存放所有 Window 和 UserControl。
  2. ViewModels/:存放所有 ViewModel 类。
  3. Models/:存放领域模型。
  4. Services/:存放服务接口和实现。
  5. Infrastructure/Helpers/ :存放RelayCommandViewModelBase等辅助类。
  6. App.xaml.cs:配置依赖注入容器和 ViewModelLocator。

7. 总结

通过本章学习,我们应该掌握:

  • MVVM 模式的核心思想和三大组件的职责。
  • 如何设计一个职责清晰的 ViewModel。
  • 通过DataContext和 ViewModelLocator 模式连接 View 和 ViewModel。
  • 使用服务层和依赖注入来解耦 ViewModel。

MVVM 模式是构建现代化 WPF 应用的标准。熟练掌握它,将极大地提升开发效率和代码质量。下一章,我们将探讨 WPF 中另一个强大的功能:值转换器(Value Converters)

相关推荐
milanyangbo7 小时前
谁生?谁死?从引用计数到可达性分析,洞悉GC的决策逻辑
java·服务器·开发语言·jvm·后端·算法·架构
星夜泊客7 小时前
C# 中的空条件运算符(?.)与空合并运算符(??)详解
c#·空条件运算符·语法糖
作孽就得先起床7 小时前
c#调Lua返回个字符串
unity·c#·lua·xlua
刘孬孬沉迷学习7 小时前
5G网络gNB与核心网(5GC)连接架构及传输协议
网络·网络协议·tcp/ip·5g·架构·udp·信息与通信
Jackson@ML7 小时前
在macOS上搭建C#集成开发环境指南
开发语言·macos·c#
文火冰糖的硅基工坊7 小时前
[人工智能-大模型-78]:模型层技术 - 深度神经网络的网络架构的演进,这不仅是一条技术路线图,更是一部 “机器如何逐步逼近人类认知方式” 的进化史诗。
人工智能·架构·dnn
小虚竹8 小时前
Tokio的多线程调度器架构:深度解析与实践
架构
月光双刀8 小时前
给旧版 .NET 也开一扇“私有之门”——ILAccess.Fody 实现原理与设计
c#·.net·fody·il·mono.cecil
张人玉8 小时前
WPF 静态样式与动态样式的定义及使用详解
ui·c#·wpf