博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用,熟悉DICOM医学影像及DICOM协议,业余时间自学JavaScript,Vue,qt,python等,具备多种混合语言开发能力。撰写博客分享知识,致力于帮助编程爱好者共同进步。欢迎关注、交流及合作,提供技术支持与解决方案。
技术合作请加本人wx(注明来自csdn):xt20160813
在 C# 开发中,特别是在 MVVM(Model-View-ViewModel)架构的客户端应用程序中,跨组件通信是实现模块化设计和松耦合的关键。Messenger
是一种基于发布-订阅模式(Publish-Subscribe Pattern)的消息传递机制,广泛应用于 WPF、WinForms 和其他 C# 框架中。本文以 Messenger.Default.Send
和 Messenger.Default.Register
为核心,结合历史对话中提到的代码示例(如 Messenger.Default.Send
),详细解析其实现原理、应用场景、技术细节,并提供完整代码示例和开发注意事项。文章特别融入国产化环境(如银河麒麟系统)的开发视角,确保内容准确、实用。
发布-订阅模式是一种设计模式,允许消息发布者(Publisher)将消息发送给订阅者(Subscriber),而无需直接耦合。Messenger
是 MVVM 框架(如 MVVM Light、Prism 或自定义实现)中常见的消息传递工具,通过 Messenger.Default.Send
和 Messenger.Default.Register
实现异步、定向的模块间通信。
在历史对话中,代码片段展示了通过 Messenger.Default.Send
发送消息,并在接收端通过 Messenger.Default.Register
捕获消息并触发 button1_Click
方法。这种机制广泛用于 UI 状态管理(如恢复按钮状态)、跨 ViewModel 通信或后台服务协调。本文将从原理、实现、代码示例到国产化开发适配,全面解析这一机制。
发布-订阅模式的核心思想是:
优点:
Messenger
是 MVVM 框架中的消息传递工具(如 MVVM Light 的 GalaSoft.MvvmLight.Messaging.Messenger
),提供以下功能:
Messenger.Default.Send(message, token)
发送特定类型的消息。Messenger.Default.Register(recipient, token, action)
注册消息处理逻辑。token
(如字符串 "fanxuan"
)实现定向消息传递,减少无关模块的处理开销。Dispatcher
或异步机制,确保线程安全。在历史对话中,Messenger.Default.Send
表示发送字符串消息 "Recovery"
,目标是注册了 "fanxuan"
令牌的订阅者。
签名:
void Send<T>(T message, object token = null);
T message
:消息内容,支持任意类型(如 string
、自定义类)。token
:可选的令牌,用于过滤接收者,通常为字符串或对象。token
和消息类型 T
的订阅者。// 发送字符串消息 "Recovery",令牌为 "fanxuan"
Messenger.Default.Send<string>("Recovery", "fanxuan");
签名:
void Register<T>(object recipient, object token, Action<T> action);
recipient
:订阅者对象(通常为 this
,表示当前类实例)。token
:与发送端匹配的令牌。action
:收到消息后执行的回调函数,接收消息内容 T
。token
和类型 T
的消息。// 注册处理 "fanxuan" 令牌的字符串消息
Messenger.Default.Register<string>(
this,
"fanxuan",
message => { Console.WriteLine($"Received: {message}"); }
);
以下是 Messenger
的工作流程(结合历史对话中的代码):
graph LR
A[Send: Messenger.Default.Send("Recovery", "fanxuan")] --> B[Messenger 中介]
B --> C[查找匹配 "fanxuan" 和 string 的订阅者]
C --> D[Register: Messenger.Default.Register(this, "fanxuan", ...)]
D --> E[执行回调: DicomOperateDispatcher.Invoke(button1_Click)]
Messenger
通常基于单例模式(Messenger.Default
),内部维护一个订阅者列表。核心实现逻辑如下:
消息注册:
Register
方法将订阅者的 {recipient, token, action}
组合存储在一个字典或列表中。T
和 token
,值为回调函数 Action
。消息发送:
Send
方法遍历订阅者列表,查找匹配 T
和 token
的注册项。action
回调,传递消息内容。线程安全:
Messenger
通常不直接处理线程切换,需结合 Dispatcher
(如 WPF 的 Dispatcher.Invoke
)确保 UI 操作在主线程执行。令牌机制:
token
)通过哈希比较(如字符串的 Equals
方法)实现高效匹配。token
为 null
,消息广播给所有订阅了类型 T
的接收者。在历史对话中,代码展示了 Messenger
在医学影像系统(DICOM 相关)中的应用,用于触发恢复按钮状态:
case IFCCallBackMSG.Recovery: // 复原按钮状态通过 ESC 键触发
{
// 发送恢复命令
Messenger.Default.Send<string>("Recovery", "fanxuan");
break;
}
IFCCallBackMSG.Recovery
状态,发送 "Recovery"
消息,目标为 "fanxuan"
通道。"fanxuan"
的模块执行状态恢复(如恢复 UI 按钮或 DICOM 数据状态)。Messenger.Default.Register<string>(
this,
"fanxuan",
message => DicomOperateDispatcher.Invoke(new Onclick(button1_Click), null, null)
);
"fanxuan"
通道的字符串消息处理。DicomOperateDispatcher.Invoke
调用 button1_Click
方法,可能是恢复 UI 按钮状态或重置 DICOM 数据。DicomOperateDispatcher.Invoke
确保 button1_Click
在 UI 线程执行,避免跨线程访问冲突。"Recovery"
表示恢复命令,可能触发 UI 重置、数据恢复或其他状态复原。"fanxuan"
(可能意为“反选”或特定模块标识)确保消息只发送给特定订阅者。message
内容进行判断,可能响应所有 "fanxuan"
通道的消息,建议添加条件判断(如 if (message == "Recovery")
)。以下是一个完整的 C# 示例,展示 Messenger
的发送和接收过程,结合 WPF 和 MVVM 架构,模拟恢复按钮状态的场景。
在项目中添加 MVVM Light NuGet 包:
Install-Package MvvmLight
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
// 模拟 ESC 键触发恢复
SendRecoveryCommand();
}
private void SendRecoveryCommand()
{
// 发送恢复消息,目标为 "fanxuan" 通道
Messenger.Default.Send<string>("Recovery", "fanxuan");
}
}
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;
using System.Windows;
public class ButtonViewModel : ViewModelBase
{
public ButtonViewModel()
{
// 注册 "fanxuan" 通道的字符串消息
Messenger.Default.Register<string>(
this,
"fanxuan",
message =>
{
// 仅处理 "Recovery" 消息
if (message == "Recovery")
{
// 在 UI 线程执行按钮状态恢复
Application.Current.Dispatcher.Invoke(() =>
{
ButtonClickHandler();
});
}
});
}
private void ButtonClickHandler()
{
// 模拟恢复按钮状态
MessageBox.Show("Button state recovered!");
// 实际逻辑:重置 UI 按钮、DICOM 数据状态等
}
}
using GalaSoft.MvvmLight.Messaging;
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 初始化 ViewModel
var mainViewModel = new MainViewModel();
var buttonViewModel = new ButtonViewModel();
}
}
MainViewModel
发送 "Recovery"
消息时,ButtonViewModel
捕获消息并执行 ButtonClickHandler
,显示提示框。Dispatcher.Invoke
确保 UI 操作在主线程执行。结合历史对话中关于银河麒麟系统的离线部署经验,以下是 Messenger
在国产化环境中的开发注意事项:
离线安装 MVVM Light:
sudo rpm -ivh mvvmlightlibs-5.4.1.rpm --nodeps --force
GalaSoft.MvvmLight.dll
。DICOM 集成:
Messenger
可用于 DICOM 系统中模块间通信(如 UI 与 PACS 数据同步)。using pydicom; // 需通过 Python 脚本解析后传递
var ds = pydicom.dcmread("mri.dcm");
Messenger.Default.Send<string>("DICOMLoaded", "fanxuan");
防火墙配置:
Messenger
用于跨进程通信(如与 Orthanc PACS 服务交互),需放行端口:sudo firewall-cmd --zone=public --add-port=4242/tcp --permanent
sudo firewall-cmd --reload
Fitten Code 集成:
// @Fitten 生成 Messenger 注册代码
Messenger.Default.Register<string>(
this,
"fanxuan",
message => { if (message == "Recovery") { /* 恢复逻辑 */ } }
);
问题 | 解决方案 |
---|---|
消息未被接收 | 检查 Register 和 Send 的 token 和消息类型是否匹配。 |
UI 线程冲突 | 使用 Dispatcher.Invoke 确保回调在主线程执行。 |
国产系统依赖缺失 | 离线安装 MVVM Light:sudo rpm -ivh mvvmlightlibs.rpm --nodeps 。 |
消息广播过广 | 添加 token (如 "fanxuan" )或条件判断(如 if (message == "Recovery") )。 |
Messenger.Default.Send
和 Messenger.Default.Register
是 C# 中基于发布-订阅模式的强大消息传递机制,适用于 MVVM 架构中的模块间通信。核心特点:
token
实现定向消息传递。Dispatcher
确保 UI 操作安全。结合历史对话中的 DICOM 系统场景,Messenger
在恢复按钮状态、跨模块通信中发挥了关键作用。在国产化环境(如银河麒麟)中,需注意离线依赖管理和防火墙配置。提供的代码示例可直接运行,适合开发者快速上手。
建议:
Messenger
实现标准化开发。