Delphi 11.3 VCL:通知进入Windows操作中心后点击不触发OnReceiveLocalNotification
TNotificationCenter点击操作中心通知不触发OnReceiveLocalNotification的问题
结论
这不是预期行为,属于Delphi 11.3中TNotificationCenter在Windows平台的实现疏漏。
原因分析
TNotificationCenter的Windows底层实现仅处理了通知首次弹出时的点击激活事件,未适配Windows操作中心中通知的激活回调逻辑。当通知被归档到操作中心后,Windows通过不同通路触发激活事件,但Delphi的默认封装未监听这部分事件,导致点击操作中心内的通知时无法触发OnReceiveLocalNotification。
解决方案
要实现点击操作中心通知也触发事件,需要手动补充Windows通知激活的处理逻辑。以下是基于原有代码的修改方案,通过直接调用WinRT Toast API实现完整的激活事件捕获:
修改后的完整代码
unit MainUnit; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Notification, Winapi.UI.Notifications, System.Win.WinRT; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); procedure ReceiveLocalNotification(Sender: TObject; ANotification: TNotification); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private NotificationCenter1: TNotificationCenter; FToastManager: ToastNotificationManager; procedure ToastActivated(Sender: TObject; const Args: ToastActivatedEventArgs); public procedure WndProc(var Message: TMessage); override; end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var N: TNotification; ToastXml: ToastNotificationXml; Toast: ToastNotification; ToastNotifier: ToastNotifier; begin // 保留原有TNotificationCenter逻辑,兼容现有代码 N := NotificationCenter1.CreateNotification; try N.Title := 'My Title'; N.AlertBody := 'My alert body'; NotificationCenter1.PresentNotification(N); finally N.Free; end; // 补充WinRT Toast通知,确保操作中心点击能触发事件 ToastXml := FToastManager.GetTemplateContent(ToastTemplateType.ToastText02); try // 设置通知标题和内容 var TextNodes := ToastXml.GetElementsByTagName('text'); TextNodes.Item(0).AppendChild(ToastXml.CreateTextNode('My Title')); TextNodes.Item(1).AppendChild(ToastXml.CreateTextNode('My alert body')); Toast := ToastNotification.Create(ToastXml); try Toast.OnActivated := ToastActivated; ToastNotifier := FToastManager.CreateToastNotifier(ExtractFileName(ParamStr(0))); try ToastNotifier.Show(Toast); finally ToastNotifier.Free; end; finally Toast.Free; end; finally ToastXml.Free; end; end; procedure TForm1.FormCreate(Sender: TObject); begin NotificationCenter1 := TNotificationCenter.Create(Self); NotificationCenter1.OnReceiveLocalNotification := ReceiveLocalNotification; // 初始化WinRT Toast管理器 FToastManager := ToastNotificationManager.Create; end; procedure TForm1.FormDestroy(Sender: TObject); begin FToastManager.Free; end; procedure TForm1.ReceiveLocalNotification(Sender: TObject; ANotification: TNotification); begin ShowMessage('I received the notification clicked event'); end; procedure TForm1.ToastActivated(Sender: TObject; const Args: ToastActivatedEventArgs); begin // 统一调用原有处理逻辑 ReceiveLocalNotification(Self, nil); end; procedure TForm1.WndProc(var Message: TMessage); begin inherited; // 处理Windows通知激活的消息传递 if Message.Msg = WM_COPYDATA then begin var CopyData := PCopyDataStruct(Message.LParam); if CopyData^.dwData = DWORD('TOAS') then // 匹配Toast激活的标识 begin ToastActivated(Self, nil); Message.Result := 1; end; end; end; end.
方案说明
- 引入WinRT的
ToastNotificationManager,直接创建Toast通知并注册OnActivated事件,该事件会捕获所有场景(包括操作中心)的点击操作。 - 重写
WndProc处理WM_COPYDATA消息,确保Windows传递的通知激活信号能被应用接收并触发处理逻辑。 - 将原有
ReceiveLocalNotification作为统一处理入口,保持代码逻辑一致性。
替代方案(修改VCL源码)
如果拥有Delphi VCL源码权限,可以修改TWinNotificationCenter的实现,补充操作中心通知激活的回调逻辑:找到TWinNotificationCenter中处理通知激活的代码,添加对Windows操作中心通知激活事件的监听,确保所有场景下的点击都能触发OnReceiveLocalNotification。
内容的提问来源于stack exchange,提问作者Matthias B
相关产品推荐
相关产品推荐

