MVVM 模式核心架構與實現細節

  • 1. MVVM 模式回顧
  • 2. ViewModel 的職責與設計
  • 2.1 狀態管理
  • 2.2 行為暴露
  • 3. View 與 ViewModel 的連接
  • 3.1 DataContext 的設置
  • 3.2 ViewModelLocator 模式
  • 4. Model 的角色與實現
  • 5. 服務層與依賴注入
  • 5.1 服務層抽象
  • 5.2 使用依賴注入容器
  • 6. 綜合實戰:構建一個完整的 MVVM 應用
  • 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 狀態示例:

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 命令示例:

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 的後台代碼中設置:

public partial class UserProfileView : Window
{
    public UserProfileView()
    {
        InitializeComponent();
        this.DataContext = new UserProfileViewModel();
    }
}

3.2 ViewModelLocator 模式

在大型應用中,為了更好地解耦和管理 ViewModel 的生命週期,通常採用 ViewModelLocator 模式,並結合依賴注入(DI)容器。

ViewModelLocator 示例:

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:

<Application.Resources>
    <local:ViewModelLocator x:Key="Locator" />
</Application.Resources>

在 View 中設置DataContext

<Window ...
        DataContext="{Binding Source={StaticResource Locator}, Path=UserProfile}">
</Window>

4. Model 的角色與實現

Model 是應用程序的領域模型,通常是簡單的 POCO(Plain Old CLR Object)對象。

Model 示例:

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 服務層抽象

public interface IUserService
{
    Task<User> GetUserAsync(int userId);
    Task SaveUserAsync(User user);
}

public class UserService : IUserService
{
    // 實現具體的數據庫或API調用邏輯
}

5.2 使用依賴注入容器

通過構造函數將服務注入到 ViewModel 中,這使得 ViewModel 更易於測試。

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)