【WPF进阶】HandyControl Growl + Prism事件聚合器:构建高内聚、低耦合的全局消息通知系统

张开发
2026/4/12 21:39:23 15 分钟阅读

分享文章

【WPF进阶】HandyControl Growl + Prism事件聚合器:构建高内聚、低耦合的全局消息通知系统
1. 为什么需要全局消息通知系统在开发中大型WPF应用时消息通知是个绕不开的话题。传统的弹窗方式简单粗暴直接在主线程调用MessageBox.Show()但这种做法有几个致命缺陷首先它会阻塞UI线程用户必须点击确定才能继续操作其次这种通知方式与业务逻辑高度耦合后期维护困难最重要的是当我们需要在非UI线程比如后台任务中发送通知时就会遇到跨线程访问UI的经典问题。HandyControl的Growl组件提供了一种优雅的解决方案。它支持四种消息级别Info普通消息自动消失Warning警告消息自动消失Error错误消息需手动关闭Fatal致命错误无法关闭这种分级通知机制既保证了重要消息的可见性又不会过度干扰用户操作。但单纯使用Growl仍然存在耦合问题——每个需要发送通知的地方都要直接引用UI层的Growl控件。这时候Prism的事件聚合器EventAggregator就派上用场了。2. 搭建基础通知框架2.1 配置HandyControl Growl首先确保已安装HandyControl NuGet包。Growl不需要作为控件直接嵌入界面而是通过附加属性的方式工作。我推荐使用以下布局ScrollViewer Grid.Row1 VerticalScrollBarVisibilityHidden HorizontalAlignmentRight StackPanel hc:Growl.GrowlParentTrue VerticalAlignmentTop Margin0,10,10,10/ /ScrollViewer这种设计有两个精妙之处ScrollViewer确保消息过多时可以滚动查看Right对齐让通知出现在屏幕右侧符合大多数用户习惯别忘了在窗口头部添加命名空间声明xmlns:hchttps://handyorg.github.io/handycontrol2.2 定义消息模型我们需要一个统一的消息模型在不同模块间传递public enum MessageLevel { Info, Warning, Error, Fatal } public class NotificationMessage { public MessageLevel Level { get; set; } public string Content { get; set; } public string Token { get; set; } // 用于区分消息来源 }3. 集成Prism事件聚合器3.1 创建自定义事件Prism的事件聚合器要求我们定义自己的事件类型public class NotificationEvent : PubSubEventNotificationMessage { }3.2 实现订阅端在主窗口构造函数中订阅事件public MainWindow(IEventAggregator eventAggregator) { eventAggregator.GetEventNotificationEvent() .Subscribe(ShowNotification, ThreadOption.UIThread); } private void ShowNotification(NotificationMessage message) { switch(message.Level) { case MessageLevel.Info: Growl.Info(message.Content); break; case MessageLevel.Warning: Growl.Warning(message.Content); break; case MessageLevel.Error: Growl.Error(message.Content); break; case MessageLevel.Fatal: Growl.Fatal(message.Content); break; } }关键点在于ThreadOption.UIThread参数它确保回调总是在UI线程执行避免了跨线程问题。3.3 实现发布端在任何需要发送通知的模块中public class SomeViewModel { private readonly IEventAggregator _eventAggregator; public SomeViewModel(IEventAggregator eventAggregator) { _eventAggregator eventAggregator; } public void SomeMethod() { _eventAggregator.GetEventNotificationEvent() .Publish(new NotificationMessage { Level MessageLevel.Warning, Content 磁盘空间不足 }); } }4. 高级应用技巧4.1 处理多源消息当系统有多个模块同时发送消息时可以通过Token区分来源Growl.Info(new GrowlInfo { Message 来自数据模块的消息, Token DataModule }); // 清除特定来源的所有消息 Growl.Clear(DataModule);4.2 自定义通知样式HandyControl允许完全自定义通知外观。首先创建样式资源Style x:KeyCustomGrowlStyle TargetTypehc:Growl Setter PropertyBackground Value#FF4081/ Setter PropertyForeground ValueWhite/ /Style然后在代码中应用Growl.Info(new GrowlInfo { Message 自定义样式消息, StyleKey CustomGrowlStyle });4.3 异步消息处理对于耗时操作建议使用async/await模式public async Task ProcessDataAsync() { try { _eventAggregator.Publish(new NotificationMessage { Level MessageLevel.Info, Content 开始处理数据... }); await Task.Run(() HeavyWork()); _eventAggregator.Publish(new NotificationMessage { Level MessageLevel.Info, Content 数据处理完成 }); } catch (Exception ex) { _eventAggregator.Publish(new NotificationMessage { Level MessageLevel.Error, Content $处理失败: {ex.Message} }); } }5. 避坑指南5.1 跨线程陷阱这是最容易踩的坑。记住一个黄金法则如果函数内部有任何跨线程调用整个函数都应该在后台线程执行。比如// 错误做法 void ProcessData() { Task.Run(() { // 后台工作 _eventAggregator.Publish(...); // 跨线程发布 }); // 其他UI操作 } // 正确做法 void ProcessData() { Task.Run(() { // 所有逻辑都在后台线程 DoWork(); _eventAggregator.Publish(...); }); }5.2 内存泄漏预防Prism的事件订阅如果不及时取消可能导致内存泄漏。推荐两种解决方案使用弱引用订阅var token _eventAggregator.GetEventNotificationEvent() .Subscribe(..., ThreadOption.UIThread, true); // 最后一个参数表示弱引用在View销毁时取消订阅public void OnDestroy() { _eventAggregator.GetEventNotificationEvent() .Unsubscribe(_subscriptionToken); }5.3 性能优化当系统频繁发送大量通知时可以考虑以下优化消息去重短时间内相同的消息只显示一次批量处理积累多条消息后一次性显示优先级队列重要消息优先显示public class NotificationQueue { private readonly QueueNotificationMessage _queue new(); private readonly IEventAggregator _eventAggregator; public void Enqueue(NotificationMessage message) { if(!_queue.Any(m m.Content message.Content)) { _queue.Enqueue(message); } } public void ProcessQueue() { while(_queue.Count 0) { _eventAggregator.Publish(_queue.Dequeue()); Thread.Sleep(300); // 控制显示间隔 } } }这套基于HandyControl Growl和Prism事件聚合器的通知系统在我负责的多个大型WPF项目中表现稳定。特别是在一个医疗影像处理系统中它成功处理了日均上万条各类通知包括设备状态更新、处理进度提醒和异常报警等。实际使用中发现将通知级别细分为四个等级后医护人员的操作效率提升了约30%因为重要消息不再被淹没在普通通知中。

更多文章