🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用,熟悉DICOM医学影像及DICOM协议,业余时间自学JavaScript,Vue,qt,python等,具备多种混合语言开发能力。撰写博客分享知识,致力于帮助编程爱好者共同进步。欢迎关注、交流及合作,提供技术支持与解决方案。\n技术合作请加本人wx(注明来自csdn):xt20160813
C# 基于 Windows 系统与 Visual Studio 2017 的 Messenger 消息传递机制详解:发布-订阅模式实现
在 C# 开发中,特别是在 Windows 平台的客户端应用程序开发中,Messenger
是一种基于发布-订阅模式(Publish-Subscribe Pattern)的消息传递机制,广泛应用于 MVVM(Model-View-ViewModel)架构,如 WPF 应用程序。它通过 Messenger.Default.Send
和 Messenger.Default.Register
实现松耦合的模块间通信,特别适合复杂 UI 交互和跨组件协作场景。本文基于 Windows 系统 和 Visual Studio 2017,结合历史对话中提到的代码示例(如 Messenger.Default.Send<string>("Recovery", "fanxuan")
),详细解析 Messenger
的实现原理、配置环境、应用场景及完整代码示例,确保内容准确、具体且可直接应用于 Windows 开发。
一、引言
在 Windows 平台的 C# 开发中,MVVM 架构常用于构建可维护、可测试的应用程序。Messenger
作为 MVVM 框架(如 MVVM Light、Prism)的核心组件,通过发布-订阅模式实现模块间通信,避免直接耦合。历史对话中的代码展示了 Messenger
在医学影像系统(DICOM 相关)中用于触发恢复按钮状态的场景,例如通过 ESC 键发送 "Recovery"
消息。本文将从 Windows 系统和 Visual Studio 2017 的开发环境出发,深入剖析 Messenger
的工作机制,提供配置指南、完整代码示例,并结合实际应用场景(如 UI 状态恢复)进行说明。
二、发布-订阅模式与 Messenger 概述
1. 发布-订阅模式
发布-订阅模式是一种解耦设计模式,核心组件包括:
- 发布者(Publisher):发送消息,不关心接收者。
- 订阅者(Subscriber):注册对特定消息的兴趣,处理接收到的消息。
- 消息中介(Broker):管理消息分发,通过消息类型或令牌(Token)过滤。
优点:
- 松耦合:模块间无需直接引用,降低依赖。
- 一对多通信:支持多个订阅者处理同一消息。
- 线程安全:结合 Windows 的 Dispatcher 机制,确保 UI 操作在主线程执行。
2. Messenger 的功能
Messenger
是 MVVM 框架(如 MVVM Light)提供的消息传递工具,核心方法包括:
- 发送消息:
Messenger.Default.Send<T>(message, token)
,发送类型为T
的消息,可指定令牌token
。 - 注册订阅:
Messenger.Default.Register<T>(recipient, token, action)
,注册处理特定类型和令牌的消息。 - 令牌机制:通过
token
(如字符串"fanxuan"
)实现定向消息传递。 - 线程支持:结合 WPF 的
Dispatcher
确保线程安全。
在历史对话中,Messenger.Default.Send<string>("Recovery", "fanxuan")
用于发送恢复命令,目标是注册了 "fanxuan"
令牌的订阅者,通常用于 UI 状态重置或 DICOM 数据操作。
三、Windows 系统与 Visual Studio 2017 环境配置
1. 系统与工具要求
- 操作系统:Windows 10(推荐 64 位,支持 Visual Studio 2017)。
- 开发工具:Visual Studio 2017(Community、Professional 或 Enterprise 版本)。
- 框架:.NET Framework 4.6.1 或更高版本(WPF 项目常用)。
- NuGet 包:MVVM Light(
MvvmLightLibs
)。
2. 配置 MVVM Light
在 Visual Studio 2017 中安装 MVVM Light:
- 打开 NuGet 包管理器:
- 在 Visual Studio 2017 中,点击 工具 > NuGet 包管理器 > 管理解决方案的 NuGet 包。
- 搜索并安装 MVVM Light:
- 搜索
MvvmLightLibs
,选择GalaSoft.MvvmLight
,点击 安装。 - 安装完成后,项目引用中将包含
GalaSoft.MvvmLight.dll
。
- 搜索
- 验证环境:
- 创建一个 WPF 项目,添加以下 using 语句:
using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Messaging;
- 确保无编译错误。
- 创建一个 WPF 项目,添加以下 using 语句:
3. 项目设置
- 创建 WPF 项目:
- 在 Visual Studio 2017 中,选择 文件 > 新建 > 项目 > Visual C# > Windows 经典桌面 > WPF 应用。
- 项目命名为
MessengerDemo
。
- 添加 ViewModel:
- 创建
ViewModels
文件夹,添加MainViewModel.cs
和ButtonViewModel.cs
。
- 创建
- 配置 Dispatcher:
- WPF 项目默认包含
Dispatcher
,无需额外配置。若需自定义 Dispatcher,可使用Application.Current.Dispatcher
。
- WPF 项目默认包含
四、Messenger 的核心方法
1. Messenger.Default.Send
签名:
void Send<T>(T message, object token = null);
- 参数:
message
:消息内容(如字符串"Recovery"
)。token
:可选的令牌,用于过滤接收者(如"fanxuan"
)。
- 作用:将消息广播给匹配
token
和类型T
的订阅者。 - 示例:
Messenger.Default.Send<string>("Recovery", "fanxuan");
2. Messenger.Default.Register
签名:
void Register<T>(object recipient, object token, Action<T> action);
- 参数:
recipient
:订阅者对象(通常为this
)。token
:与发送端匹配的令牌。action
:收到消息后的回调函数。
- 作用:注册处理特定类型和令牌的消息。
- 示例:
Messenger.Default.Register<string>( this, "fanxuan", message => { /* 处理逻辑 */ } );
3. 消息传递流程
graph LR
A[Send: Messenger.Default.Send<string>("Recovery", "fanxuan")] --> B[Messenger 中介]
B --> C[查找匹配 "fanxuan" 和 string 的订阅者]
C --> D[Register: Messenger.Default.Register<string>(this, "fanxuan", ...)]
D --> E[执行回调: Dispatcher.Invoke(button1_Click)]
五、Messenger 实现原理
Messenger
基于单例模式(Messenger.Default
),内部维护订阅者列表。核心逻辑包括:
- 订阅管理:
Register
方法将{recipient, token, action}
存储在字典中,键为消息类型T
和token
。
- 消息分发:
Send
方法遍历订阅者列表,匹配T
和token
,调用对应的action
。
- 线程安全:
- 在 WPF 中,
Dispatcher.Invoke
确保 UI 操作在主线程执行。
- 在 WPF 中,
- 令牌匹配:
- 令牌通过
Equals
方法比较(如字符串的哈希比较),实现高效过滤。
- 令牌通过
六、Messenger 在历史对话中的应用
历史对话中的代码展示了 Messenger
在医学影像系统中的应用,用于触发 UI 恢复:
发送端:
case ItCallBackMSG.Recovery: // 通过 ESC 键触发恢复
{
Messenger.Default.Send<string>("Recovery", "fanxuan");
break;
}
- 场景:用户按 ESC 键触发
ItCallBackMSG.Recovery
,发送"Recovery"
消息到"fanxuan"
通道。 - 目的:通知订阅者执行 UI 按钮状态恢复或 DICOM 数据重置。
接收端:
Messenger.Default.Register<string>(
this,
"fanxuan",
message => DcmOperateDispatcher.Invoke(new Onclick(button1_Click), null, null)
);
- 场景:某个类(可能是 ViewModel 或 View)注册
"fanxuan"
通道,收到消息后调用button1_Click
。 - 线程安全:
DcmOperateDispatcher.Invoke
确保回调在 UI 线程执行。 - 改进建议:添加消息内容判断(如
if (message == "Recovery")
)以提高特异性。
七、完整代码示例
以下是一个基于 WPF 的完整示例,展示 Messenger
在 Windows 系统中的应用,模拟 ESC 键触发按钮状态恢复。
1. 项目结构
- MainWindow.xaml:主窗口,包含一个按钮。
- MainViewModel.cs:发送恢复消息。
- ButtonViewModel.cs:接收消息并处理按钮状态。
2. XAML(MainWindow.xaml)
<Window x:Class="MessengerDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Messenger Demo" Height="200" Width="300">
<Grid>
<Button Content="Recover State" Width="100" Height="30"
Command="{Binding RecoverCommand}"/>
</Grid>
</Window>
3. MainViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
public class MainViewModel : ViewModelBase
{
public RelayCommand RecoverCommand { get; private set; }
public MainViewModel()
{
// 初始化命令,模拟 ESC 键触发
RecoverCommand = new RelayCommand(SendRecoveryCommand);
}
private void SendRecoveryCommand()
{
// 发送恢复消息到 "fanxuan" 通道
Messenger.Default.Send<string>("Recovery", "fanxuan");
}
}
4. ButtonViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;
using System.Windows;
public class ButtonViewModel : ViewModelBase
{
public ButtonViewModel()
{
// 注册 "fanxuan" 通道的消息
Messenger.Default.Register<string>(
this,
"fanxuan",
message =>
{
if (message == "Recovery")
{
// 在 UI 线程执行恢复逻辑
Application.Current.Dispatcher.Invoke(() =>
{
RecoverButtonState();
});
}
});
}
private void RecoverButtonState()
{
// 模拟恢复按钮状态
MessageBox.Show("Button state recovered!");
// 实际逻辑:重置 UI 或 DICOM 数据
}
}
5. 主程序(App.xaml.cs)
using GalaSoft.MvvmLight.Messaging;
using System.Windows;
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 初始化 ViewModel
var mainViewModel = new MainViewModel();
var buttonViewModel = new ButtonViewModel();
// 设置主窗口
var mainWindow = new MainWindow
{
DataContext = mainViewModel
};
mainWindow.Show();
}
}
6. 运行结果
- 点击窗口中的“Recover State”按钮,触发
RecoverCommand
,发送"Recovery"
消息。 ButtonViewModel
捕获消息,显示提示框“Button state recovered!”。- 实际应用中,可在
RecoverButtonState
中实现 UI 重置或 DICOM 数据恢复逻辑。
八、在 Windows 系统中的开发注意事项
Visual Studio 2017 配置:
- 确保安装 .NET Framework 4.6.1 或更高版本。
- 使用 NuGet 包管理器安装
MvvmLightLibs
(版本 5.4.1 兼容 VS2017)。 - 若网络受限,可下载离线 NuGet 包:
nuget install MvvmLightLibs -Version 5.4.1 -OutputDirectory .\Packages
调试技巧:
- 在 Visual Studio 2017 中设置断点,检查
Messenger.Default.Send
和Register
的执行流程。 - 使用 即时窗口查看
Messenger
订阅者列表(需自定义Messenger
实现)。 - 验证
Dispatcher.Invoke
是否正确切换到 UI 线程。
- 在 Visual Studio 2017 中设置断点,检查
性能优化:
- 避免频繁注册/注销订阅者,建议在 ViewModel 构造时注册,析构时注销:
public override void Cleanup() { Messenger.Default.Unregister(this); base.Cleanup(); }
- 使用具体
token
(如"fanxuan"
)减少消息广播开销。
- 避免频繁注册/注销订阅者,建议在 ViewModel 构造时注册,析构时注销:
DICOM 集成:
- 在医学影像系统中,
Messenger
可用于通知 UI 更新 DICOM 参数(如 FOV、TR)。 - 示例:发送 DICOM 文件加载完成消息:
Messenger.Default.Send<string>("DICOMLoaded", "fanxuan");
- 在医学影像系统中,
九、常见问题与解决方案
问题 | 解决方案 |
---|---|
消息未被接收 | 确认 Send 和 Register 的 token 和消息类型一致。 |
UI 更新失败 | 使用 Application.Current.Dispatcher.Invoke 确保回调在 UI 线程执行。 |
NuGet 安装失败 | 离线下载 MvvmLightLibs 包,导入项目:nuget install MvvmLightLibs 。 |
消息误触发 | 在 Register 的回调中添加条件判断:if (message == "Recovery") 。 |
十、总结
Messenger.Default.Send
和 Messenger.Default.Register
是 C# 中基于发布-订阅模式的强大消息传递机制,特别适合 Windows 平台的 WPF 开发。基于 Visual Studio 2017 环境,本文提供了完整的配置指南和代码示例,展示了 Messenger
在 UI 状态恢复(如历史对话中的 ESC 键触发)中的应用。核心特点:
- 松耦合:通过
token
实现定向通信。 - 线程安全:结合
Dispatcher
确保 UI 操作安全。 - 易用性:MVVM Light 提供简单 API,适合快速开发。
在 Windows 系统和 Visual Studio 2017 中,开发者可通过 NuGet 快速集成 MVVM Light,结合 WPF 的 Dispatcher 机制实现高效通信。建议在实际开发中添加消息内容验证和订阅者管理,以提高代码健壮性。
参考资料
- MVVM Light 文档:http://www.mvvmlight.net/
- Visual Studio 2017 文档:https://docs.microsoft.com/en-us/visualstudio/
- C# 委托与事件:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/events/