我正在为 Delphi 创建一个库,以便使用
Winapi.UI.Notifications
中的接口更轻松地实现 Windows 11 toast 通知。
我想使用
IToastNotifier
将自定义通知发布到操作中心以显示通知,并能够接收通知被取消或激活(单击)时的事件。值得庆幸的是, IToastNotification
具有 add_Activated()
、add_Dismissed()
和 add_Failed()
方法来注册这些事件触发时的回调。
在 C# 中,添加它们相当容易,如下所示:
public void ShowToastNotification()
{
// Construct the toast content with data bound fields
var content = new ToastContentBuilder()
.AddText("Notification title")
.GetToastContent();
// Generate the toast notification
var toast = new ToastNotification(content.GetXml());
toast.Activated += Toast_Activated;
// Show the toast notification to the user
ToastNotificationManager.CreateToastNotifier().Show(toast);
}
private void Toast_Activated(ToastNotification sender, object args)
{
ShowMessageDialog($"Toast activated!");
}
为了在 Delphi 中做同样的事情,我编写了以下代码片段:
unit Unit1;
interface
uses
Winapi.Windows, Vcl.Forms, Winapi.ui.Notifications,
Winapi.CommonTypes, Winapi.Winrt, Vcl.Dialogs, Winapi.DataRT;
type
TToastActivatedHandler = class(TInterfacedObject, TypedEventHandler_2__IToastNotification__IInspectable)
procedure Invoke(sender: IToastNotification; args: IInspectable); safecall;
end;
TToastDismissHandler = class(TInterfacedObject, TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs)
procedure Invoke(sender: IToastNotification; args: IToastDismissedEventArgs); safecall;
end;
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
end;
var
Form1: TForm1;
ActivateHandle: TToastActivatedHandler;
DismissHandle: TToastDismissHandler;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
HStr: HSTRING;
S: string;
Instance: IInspectable;
Notifier: IToastNotifier;
begin
// Notification maanger
const xml = '<toast activationType="protocol">'+
' <visual>'+
' <binding template="ToastGeneric">'+
' <text>Hello world!</text>'+
' <text>This is a test notification.</text>'+
' <image src="C:\Windows\System32\@facial-recognition-windows-hello.gif" placement="hero" alt=""/>'+
' </binding>'+
' </visual>'+
'</toast>';
// Create XML
S := 'Windows.Data.Xml.Dom.XmlDocument';
WindowsCreateString(PChar(S), Length(S), HStr);
try
RoActivateInstance(HStr, Instance);
finally
WindowsDeleteString(HStr);
end;
// Load XML
WindowsCreateString(PChar(xml), Length(xml), HStr);
try
(Instance as Xml_Dom_IXmlDocumentIO).LoadXml( HStr );
finally
WindowsDeleteString(HStr);
end;
// Create interfaces
S := 'App.Test';
WindowsCreateString(PChar(S), Length(S), HStr);
try
Notifier := TToastNotificationManager.CreateToastNotifier(HStr);
finally
WindowsDeleteString(HStr);
end;
const Notification = TToastNotification.CreateToastNotification( Instance as Xml_Dom_IXmlDocument );
// Create activator
ActivateHandle := TToastActivatedHandler.Create;
DismissHandle := TToastDismissHandler.Create;
// Prepare
Notification.add_Activated( ActivateHandle );
Notification.add_Dismissed( DismissHandle );
// Show
Notifier.Show( Notification );
end;
{ TToastActivatedHandler2 }
procedure TToastDismissHandler.Invoke(sender: IToastNotification;
args: IToastDismissedEventArgs);
begin
ShowMessage('Toast was dismissed!');
end;
{ TToastActivatedHandler }
procedure TToastActivatedHandler.Invoke(sender: IToastNotification;
args: IInspectable);
begin
ShowMessage('Toast was activated');
end;
end.
出现以下通知:
但是当我单击通知时,永远不会调用
Invoke()
方法,我不确定为什么。我也尝试了add_Dismissed()
,但它也出现了同样的问题。
我完全不知道该尝试什么。必须有一种方法可以调用调用方法,但我不确定如何实现这一点。
注意: 运行上面的代码不需要单元文件,我只从其中获取了显示此示例所需的片段。但完整的库可以在 GitHub here 上找到。
我已经解决这个问题了!
接口似乎非常依赖其序列化的 GUID,如果没有将正确的 ID 添加到类中,注册将会失败。
由于
TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs
是TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs_Delegate_Base
的子接口,我以为它也会继承它的GUID,但似乎事实并非如此。
该修复非常容易应用,只需将
TypedEventHandler_2__IToastNotification__IInspectable_Delegate_Base
和 TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs_Delegate_Base
接口类分别添加到 TToastActivatedHandler
和 TToastDismissedHandler
对象即可。
以下是正确的声明:
TToastActivatedHandler = class(TInterfacedObject, TypedEventHandler_2__IToastNotification__IInspectable,
TypedEventHandler_2__IToastNotification__IInspectable_Delegate_Base)
public
procedure Invoke(sender: IToastNotification; args: IInspectable); safecall;
end;
TToastDismissedHandler = class(TInterfacedObject, TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs,
TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs_Delegate_Base)
public
procedure Invoke(sender: IToastNotification; args: IToastDismissedEventArgs); safecall;
end;