WPF框架---MvvmLight介绍

目录

[1. MvvmLight 框架准备](#1. MvvmLight 框架准备)

[2. MvvmLight 中的相关基类](#2. MvvmLight 中的相关基类)

[3. MvvmLight 中的数据绑定与通知](#3. MvvmLight 中的数据绑定与通知)

[a. 核心功能](#a. 核心功能)

[b. 关键方法与属性](#b. 关键方法与属性)

[c. 完整示例](#c. 完整示例)

[d. 高级用法](#d. 高级用法)

[4. MvvmLight 中的命令对象](#4. MvvmLight 中的命令对象)

[a. 命令对象的作用](#a. 命令对象的作用)

[b. 核心接口:ICommand](#b. 核心接口:ICommand)

[c. MvvmLight 中的 RelayCommand](#c. MvvmLight 中的 RelayCommand)

[d. 动态更新命令的可执行状态](#d. 动态更新命令的可执行状态)

[e. 高级用法](#e. 高级用法)

[5. Messenger 对象使用](#5. Messenger 对象使用)

[a. Messenger 的核心作用](#a. Messenger 的核心作用)

[b. MvvmLight 中的 Messenger 实现](#b. MvvmLight 中的 Messenger 实现)

[c. 基本使用场景](#c. 基本使用场景)

[d. 高级用法](#d. 高级用法)

[6. DispatcherHelper 对象使用](#6. DispatcherHelper 对象使用)

[7. SimpleIoc 对象使用](#7. SimpleIoc 对象使用)

[a. SimpleIoc 的核心作用](#a. SimpleIoc 的核心作用)

[b. 核心方法与属性](#b. 核心方法与属性)

[c. 基本使用步骤](#c. 基本使用步骤)

[d. 高级用法](#d. 高级用法)

[e. 与 ViewModelLocator 结合使用](#e. 与 ViewModelLocator 结合使用)


  1. MvvmLight 框架准备
  • 作用: 快速搭建 MVVM 架构的应用程序,简化数据绑定、命令和消息传递。
  • 步骤 : 通过 NuGet 安装 MvvmLightLibs 包。

2. MvvmLight 中的相关基类

  • 核心基类:
    • ViewModelBase: ViewModel 基类,实现 INotifyPropertyChanged
cs 复制代码
public class MainViewModel : ViewModelBase
{
    private string _name;
    public string Name
    {
        get => _name;
        set => Set(ref _name, value); // 自动触发 PropertyChanged
    }
}
    • ObservableObject: 轻量级可观察对象。
    • RelayCommand: 命令对象基类。

3. MvvmLight 中的数据绑定与通知

a. 核心功能

ViewModelBase 继承自 ObservableObject,并实现了 INotifyPropertyChanged 接口,主要负责:

  • 属性变更通知:当 ViewModel 的某个属性值发生变化时,自动通知 UI 更新。
  • 简化代码 :通过 Set 方法简化属性定义,避免手动触发 PropertyChanged 事件。
  • 设计模式支持 :提供静态属性 IsInDesignMode,用于区分代码是在设计时(如 Visual Studio 设计器)还是运行时执行。
b. 关键方法与属性

(1) Set<T>(ref T field, T value, [CallerMemberName] string propertyName = null)

  • 作用 :在属性的 set 方法中调用此方法,自动比较新旧值,若不同则更新字段并触发 PropertyChanged 事件。
  • 示例
cs 复制代码
private string _name;
public string Name
{
    get => _name;
    set => Set(ref _name, value); // 自动触发通知
}

(2)RaisePropertyChanged(string propertyName)

  • 作用 :手动触发某个属性的 PropertyChanged 事件。
  • 场景:当某个属性的值依赖于其他属性时,手动通知 UI 更新。
cs 复制代码
public string FullName => $"{FirstName} {LastName}";

private string _firstName;
public string FirstName
{
    get => _firstName;
    set
    {
        Set(ref _firstName, value);
        RaisePropertyChanged(nameof(FullName)); // 通知 FullName 属性变化
    }
}

(3) IsInDesignMode

  • 作用:静态属性,判断当前代码是否在设计器(如 Visual Studio 或 Blend)中运行。
  • 用途:在设计时提供假数据,避免调用真实服务或数据库。
cs 复制代码
public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        if (IsInDesignMode)
            Name = "Design Mode Sample"; // 设计器显示假数据
        else
            LoadRealData(); // 运行时加载真实数据
    }
}
c. 完整示例
cs 复制代码
using GalaSoft.MvvmLight;

public class UserViewModel : ViewModelBase
{
    private string _userName;
    private int _age;

    public string UserName
    {
        get => _userName;
        set => Set(ref _userName, value);
    }

    public int Age
    {
        get => _age;
        set => Set(ref _age, value);
    }

    // 计算属性(依赖其他属性)
    public string UserInfo => $"{UserName} (Age: {Age})";

    // 当 UserName 或 Age 变化时,手动通知 UserInfo 更新
    protected override void OnPropertyChanged(string propertyName = null)
    {
        base.OnPropertyChanged(propertyName);
        if (propertyName == nameof(UserName) || propertyName == nameof(Age))
            RaisePropertyChanged(nameof(UserInfo));
    }
}
d. 高级用法

(1)批量通知多个属性

通过 RaisePropertyChanged(null) 或指定空字符串,通知所有属性更新(慎用,可能影响性能):

cs 复制代码
public void ResetAllProperties()
{
    _userName = "Default";
    _age = 0;
    RaisePropertyChanged(""); // 通知所有属性更新
}

(2) 继承与扩展

可继承 ViewModelBase 并添加通用逻辑(如日志记录、验证):

cs 复制代码
public abstract class CustomViewModelBase : ViewModelBase
{
    protected void LogPropertyChange(string propertyName)
    {
        Debug.WriteLine($"属性 {propertyName} 已更新");
    }

    public override void RaisePropertyChanged(string propertyName = null)
    {
        base.RaisePropertyChanged(propertyName);
        LogPropertyChange(propertyName);
    }
}

4. MvvmLight 中的命令对象

a. 命令对象的作用
  • 解耦 UI 与业务逻辑:将用户操作(如点击按钮)映射到 ViewModel 的方法。
  • 控制可执行状态 :根据条件动态启用或禁用 UI 元素(例如按钮的 IsEnabled)。
  • 支持参数传递:允许从 UI 传递参数到 ViewModel(如选中项的 ID)。
b. 核心接口:ICommand

所有命令对象均实现 System.Windows.Input.ICommand 接口,其定义如下:

cs 复制代码
public interface ICommand
{
    event EventHandler CanExecuteChanged; // 通知命令可执行状态变化
    bool CanExecute(object parameter);    // 判断命令是否可执行
    void Execute(object parameter);       // 执行命令逻辑
}
c. MvvmLight 中的 RelayCommand

MvvmLight 提供 RelayCommandRelayCommand<T> 类,简化了 ICommand 的实现。

(1) 基本用法(无参数)

cs 复制代码
public class MainViewModel : ViewModelBase
{
    public RelayCommand SaveCommand { get; }

    public MainViewModel()
    {
        // 初始化命令:绑定方法 + 可执行条件
        SaveCommand = new RelayCommand(SaveData, CanSave);
    }

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

    private bool CanSave()
    {
        return !string.IsNullOrEmpty(Name); // 仅当 Name 非空时按钮可用
    }
}

(2) 支持参数传递(RelayCommand<T>)

cs 复制代码
public RelayCommand<string> FilterCommand { get; }

public MainViewModel()
{
    FilterCommand = new RelayCommand<string>(param => ApplyFilter(param), param => !string.IsNullOrEmpty(param));
}

private void ApplyFilter(string keyword)
{
    // 根据关键字过滤数据
}
d. 动态更新命令的可执行状态

(1) 手动触发更新

CanExecute 依赖的属性变化时,调用 RelayCommandRaiseCanExecuteChanged 方法:

cs 复制代码
private string _name;
public string Name
{
    get => _name;
    set
    {
        Set(ref _name, value);
        SaveCommand.RaiseCanExecuteChanged(); // 触发重新检查 CanSave
    }
}

(1) 自动触发更新

利用 ViewModelBaseSet 方法自动触发属性变更通知,无需手动调用 RaiseCanExecuteChanged

cs 复制代码
private string _name;
public string Name
{
    get => _name;
    set => Set(ref _name, value); // Set 方法已自动触发 PropertyChanged
}

// CanSave 方法中依赖 Name 属性
private bool CanSave() => !string.IsNullOrEmpty(Name);
e. 高级用法

(1) 异步命令

直接在命令中执行异步操作时,需注意线程安全(通过 DispatcherHelper):

cs 复制代码
public RelayCommand LoadDataCommand { get; }

public MainViewModel()
{
    LoadDataCommand = new RelayCommand(async () => await LoadDataAsync());
}

private async Task LoadDataAsync()
{
    try
    {
        IsLoading = true;
        var data = await _dataService.GetData();
        // 更新 UI(确保在 UI 线程)
        DispatcherHelper.CheckBeginInvokeOnUI(() => DataList = data);
    }
    finally
    {
        IsLoading = false;
    }
}

(2) 复合命令

将多个命令组合成一个逻辑操作:

cs 复制代码
public RelayCommand SubmitAllCommand { get; }

public MainViewModel()
{
    SubmitAllCommand = new RelayCommand(() =>
    {
        SaveCommand.Execute(null);
        LogCommand.Execute(null);
    }, () => SaveCommand.CanExecute(null) && LogCommand.CanExecute(null));
}

(3) 命令的泛型约束

cs 复制代码
public RelayCommand<int> DeleteItemCommand { get; }

public MainViewModel()
{
    DeleteItemCommand = new RelayCommand<int>(id => DeleteItem(id), id => id > 0);
}

private void DeleteItem(int itemId)
{
    // 删除指定 ID 的项
}

5. Messenger 对象使用

MVVM 模式 中,Messenger 是一个用于实现 松耦合通信 的核心组件,尤其在 MvvmLight 框架 中被广泛使用。它允许不同组件(如 ViewModel、View、服务)之间通过消息进行通信,而无需直接引用彼此,从而降低依赖、提升代码可维护性。

a. Messenger 的核心作用
  • 解耦组件通信:组件无需持有对方引用,通过消息订阅/发布机制交互。
  • 跨层级传递数据:例如从子 ViewModel 通知父 ViewModel,或跨页面传递状态。
  • 支持复杂场景:如广播通知、请求-响应模式、事件聚合等。
b. MvvmLight 中的 Messenger 实现

MvvmLight 的 Messenger 类是一个静态单例(Messenger.Default),提供消息的注册、发送和注销功能。其核心方法如下:

|-----------------------------------------------------------------|-------------------------------------------------------|
| 方法 | 作用 |
| Register<TMessage>(object recipient, Action<TMessage> action) | 订阅类型为 TMessage 的消息。recipient 为接收者标识(通常为 this )。 |
| Send<TMessage>(TMessage message) | 发送一条 TMessage 类型的消息,所有订阅者会收到通知。 |
| Unregister(object recipient) | 注销某个接收者的所有消息订阅,避免内存泄漏。 |

c. 基本使用场景

(1) 发送简单通知消息

  • 场景:ViewModel A 完成数据加载后,通知 ViewModel B 刷新界面。

发送消息方

cs 复制代码
// 发送一个无参数通知
Messenger.Default.Send(new NotificationMessage("DataLoaded"));

接收消息方(ViewModel B)

cs 复制代码
public ViewModelB()
{
    // 注册接收 NotificationMessage 类型的消息
    Messenger.Default.Register<NotificationMessage>(this, message =>
    {
        if (message.Notification == "DataLoaded")
        {
            RefreshData();
        }
    });
}

(2) 传递数据对象

  • 场景:用户选择某条数据后,跨页面传递选中项。

定义消息类型

cs 复制代码
public class ItemSelectedMessage
{
    public int ItemId { get; set; }
    public ItemSelectedMessage(int id) => ItemId = id;
}

发送方:

cs 复制代码
// 用户选择某项后发送消息
Messenger.Default.Send(new ItemSelectedMessage(selectedItem.Id));

接收方:

cs 复制代码
Messenger.Default.Register<ItemSelectedMessage>(this, message =>
{
    LoadItemDetails(message.ItemId); // 根据 ItemId 加载详情
});
d. 高级用法

(1) 消息令牌(Token)

  • 作用:区分相同消息类型的不同用途,避免消息冲突。

发送带令牌的消息

cs 复制代码
// 发送消息时指定令牌
Messenger.Default.Send(
    new NotificationMessage("UpdateChart"), 
    "ChartToken" // 令牌标识
);

接收指定令牌的消息

cs 复制代码
Messenger.Default.Register<NotificationMessage>(
    this, 
    message => UpdateChartData(), 
    "ChartToken" // 仅接收带有此令牌的消息
);

(2) 泛型消息

  • 场景:传递强类型数据,避免类型转换。

定义泛型消息

cs 复制代码
public class GenericMessage<T>
{
    public T Content { get; }
    public GenericMessage(T content) => Content = content;
}

发送泛型消息

cs 复制代码
var data = new List<User>();
Messenger.Default.Send(new GenericMessage<List<User>>(data));

接收泛型消息

cs 复制代码
Messenger.Default.Register<GenericMessage<List<User>>>(this, message =>
{
    UserList = message.Content; // 直接获取 List<User>
});

(3) 双向通信(请求-响应模式)

  • 场景:ViewModel A 请求数据,ViewModel B 响应返回结果。

发送请求消息

cs 复制代码
public class DataRequestMessage
{
    public Action<string> Callback { get; set; } // 定义回调委托
}

// 发送请求,并注册回调
Messenger.Default.Send(new DataRequestMessage 
{ 
    Callback = response => HandleResponse(response) 
});

接收请求并响应

cs 复制代码
Messenger.Default.Register<DataRequestMessage>(this, message =>
{
    var data = FetchDataFromService(); // 获取数据
    message.Callback?.Invoke(data);    // 执行回调
});

6. DispatcherHelper 对象使用

  • 作用: 在非 UI 线程更新 UI。
  • 初始化:
cs 复制代码
DispatcherHelper.Initialize(); // 在 App.xaml.cs 中调用
  • 使用:
cs 复制代码
Task.Run(() =>
         {
             // 后台线程操作
             DispatcherHelper.CheckBeginInvokeOnUI(() =>
                                          {
                                              // 更新 UI 元素
                                              StatusText = "Processing...";
                                          });
         });

7. SimpleIoc 对象使用

SimpleIocMvvmLight 框架中提供的一个轻量级依赖注入(Dependency Injection, DI)容器,用于管理应用程序中各个组件(如 ViewModel、服务、数据源)的依赖关系。它通过 控制反转(IoC)依赖注入 机制,帮助开发者实现代码解耦、提高可测试性和可维护性。

a. SimpleIoc 的核心作用
  • 解耦组件依赖:将类的依赖关系从代码中抽离,通过容器统一管理。
  • 单例生命周期管理:默认以单例模式提供实例,避免重复创建对象。
  • 简化实例获取:通过接口或类型直接获取已注册的实例。
  • 支持构造函数注入:自动解析构造函数参数,完成依赖注入。
b. 核心方法与属性

以下是 SimpleIoc 的常用方法及其功能:

|----------------------------------|------------------------------------|
| 方法/属性 | 作用 |
| Register<TInterface, TClass>() | 注册接口 TInterface 和其实现类 TClass 。 |
| Register<TClass>() | 直接注册类型 TClass (无接口)。 |
| GetInstance<T>() | 获取类型 T 的实例(已注册的接口或类)。 |
| ContainsCreated<T>() | 检查类型 T 的实例是否已被创建。 |
| Reset() | 重置容器,清空所有注册和实例(用于测试或重新初始化)。 |
| IsRegistered<T>() | 检查类型 T 是否已注册。 |

c. 基本使用步骤

(1) 注册服务与 ViewModel

在应用程序启动时(如 ViewModelLocatorApp.xaml.cs 中),注册所有依赖项:

cs 复制代码
public class ViewModelLocator
{
    public ViewModelLocator()
    {
        // 注册服务(接口 + 实现类)
        SimpleIoc.Default.Register<IDataService, DataService>();
        // 注册 ViewModel(无接口)
        SimpleIoc.Default.Register<MainViewModel>();
    }

    // 提供 ViewModel 实例的公共属性
    public MainViewModel Main => SimpleIoc.Default.GetInstance<MainViewModel>();
}

(2) 获取实例

通过 GetInstance<T> 方法获取已注册的实例:

cs 复制代码
// 获取 ViewModel 实例
var mainVM = SimpleIoc.Default.GetInstance<MainViewModel>();

// 获取服务实例
var dataService = SimpleIoc.Default.GetInstance<IDataService>();

(3) 构造函数注入

当注册的类(如 ViewModel)依赖其他服务时,SimpleIoc 会自动解析构造函数参数:

cs 复制代码
public class MainViewModel : ViewModelBase
{
    private readonly IDataService _dataService;

    // 构造函数依赖注入:自动传入已注册的 IDataService 实例
    public MainViewModel(IDataService dataService)
    {
        _dataService = dataService;
    }
}
d. 高级用法

(1) 单例模式 vs. 瞬时模式

单例模式(默认):整个应用程序生命周期内只创建一个实例。

cs 复制代码
// 默认单例模式
SimpleIoc.Default.Register<IDataService, DataService>();

瞬时模式 :每次调用 GetInstance 时创建新实例。

cs 复制代码
SimpleIoc.Default.Register<IDataService, DataService>(createInstanceImmediately: false);
var service = SimpleIoc.Default.GetInstance<IDataService>(); // 每次返回新实例

(2) 手动指定实例

允许直接注册一个已存在的对象实例:

cs 复制代码
var logger = new FileLogger();
SimpleIoc.Default.Register<ILogger>(() => logger); // 注册现有实例

(3) 依赖覆盖

在测试中,可以替换实现类以注入 Mock 对象:

cs 复制代码
// 生产环境注册真实服务
SimpleIoc.Default.Register<IDataService, DataService>();

// 测试环境覆盖为 Mock 服务
SimpleIoc.Default.Unregister<IDataService>();
SimpleIoc.Default.Register<IDataService, MockDataService>();
e. 与 ViewModelLocator 结合使用

ViewModelLocator 是 MvvmLight 中用于集中管理 ViewModel 的类,通常与 SimpleIoc 配合使用,通过 XAML 绑定 ViewModel。

(1) 定义 ViewModelLocator

cs 复制代码
public class ViewModelLocator
{
    public ViewModelLocator()
    {
        // 注册服务和 ViewModel
        SimpleIoc.Default.Register<IDataService, DataService>();
        SimpleIoc.Default.Register<MainViewModel>();
    }

    // 暴露 ViewModel 属性供 XAML 绑定
    public MainViewModel Main => SimpleIoc.Default.GetInstance<MainViewModel>();
}

(2) 在 XAML 中声明资源

App.xaml 中合并 ViewModelLocator

cs 复制代码
<Application.Resources>
  <ResourceDictionary>
    <vm:ViewModelLocator x:Key="Locator" />
  </ResourceDictionary>
</Application.Resources>

(3) 在 View 中绑定 ViewModel

cs 复制代码
<Window 
    DataContext="{Binding Main, Source={StaticResource Locator}}">
    <!-- UI 元素绑定到 MainViewModel 的属性 -->
</Window>

相关推荐
好看资源平台3 分钟前
手写识别革命:Manus AI如何攻克多语言混合识别难题(二)
开发语言·人工智能·php
Hopebearer_17 分钟前
vue3中ref和reactive的区别
开发语言·前端·javascript·vue.js·前端框架·ecmascript
lly20240630 分钟前
CentOS Docker 安装指南
开发语言
我命由我1234534 分钟前
Java Maven 项目问题:com.diogonunes:JColor:jar:5.5.1 was not found in...
java·开发语言·java-ee·maven·intellij-idea·jar·intellij idea
烂蜻蜓2 小时前
深入理解 HTML 元素:构建网页的基础
开发语言·前端·css·html·html5
Jason-河山5 小时前
如何在PHP爬虫中处理异常情况的详细指南
开发语言·爬虫·php
kesteler6 小时前
R语言——数据类型
开发语言·r语言
Jason-河山6 小时前
利用PHP爬虫根据关键词获取17网(17zwd)商品列表:实战指南
开发语言·爬虫·php
论迹6 小时前
【JavaEE】-- 多线程(初阶)4
java·开发语言·java-ee
VBA63376 小时前
VBA之Word应用第三章第七节:文档Document对象的方法(一)
开发语言