Search code examples
delphidelphi-xe8

How to add a text node to a toast notification


This toast notification works very well from the desktop with XE8 With Windows 10, but I can not figure out how to add a text line to the notification. iTitle is displayed but iMessage is not. This is all new to me so I do not know which direction to pursue.

Fifth edit....

Remy's excellent show toast procedure is a big improvement over the original Embaracdero code, but I don't think Remy actually tested the code because it would not compile as written. I had to change TWindowString to TWindowsString and IXmlNode to Xml_Dom_IXmlNode to get it to compile.

The following actually compiles but it generates an access violation in the GetActivationFactory function.

If we can get this to function correctly it will be a big improvement over the original Embarcadero code and should be of value to other developers.

 procedure TForm1.ShowToast(const AMessage: String; const ATitle: String = '');
{ Send a Toast Notification }
var
  LINotificationManagerStatics: IToastNotificationManagerStatics;
  LToast: IToastNotification;
  LToastFactory: IToastNotificationFactory;
  LToastNotifier: IToastNotifier;
  LToastTemplateType: ToastTemplateType;
  LAccepted: TAcceptedEventHandler;
  LXMLTemplate: Xml_Dom_IXmlDocument;
  iTextNode: Xml_Dom_IXmlNode;
  LTextNodeList: Xml_Dom_IXmlNodeList;

  function GetActivationFactory(const ClassId: String; const Iid: String): IInspectable;
  begin
    OleCheck(RoGetActivationFactory(TWindowsString(ClassId), TGUID.Create(Iid), Result));
   // This produces an access violation at run time
  end;

begin
  LINotificationManagerStatics := GetActivationFactory(SToastNotificationManager, '{50AC103F-D235-4598-BBEF-98FE4D1A3AD4}') as IToastNotificationManagerStatics;
  LToastNotifier := LINotificationManagerStatics.CreateToastNotifier(TWindowsString(Edit1.Text));
  if ATitle <> '' then begin
    LToastTemplateType := ToastTemplateType.ToastText02;
  end else begin
    LToastTemplateType := ToastTemplateType.ToastText01;
  end;
  LXMLTemplate := LINotificationManagerStatics.GetTemplateContent(LToastTemplateType);
  LTextNodeList := LXMLTemplate.getElementsByTagName(TWindowsString('text'));
  if ATitle <> '' then
  begin
    LTextNodeList.Item(0).AppendChild(LXMLTemplate.CreateTextNode(TWindowsString(ATitle)) as Xml_Dom_IXmlNode);
    iTextNode := LTextNodeList.Item(1);
  end else begin
    iTextNode := LTextNodeList.Item(0);
  end;
  iTextNode.AppendChild(LXMLTemplate.CreateTextNode(TWindowsString(AMessage)) as Xml_Dom_IXmlNode);
  LToastFactory := GetActivationFactory(SToastNotification, '{04124B20-82C6-4229-B109-FD9ED4662B53}') as IToastNotificationFactory;
  LToast := LToastFactory.CreateToastNotification(LXMLTemplate);
  LAccepted := TAcceptedEventHandler.Create;
  LToast.add_Activated(LAccepted);
  LToastNotifier.Show(LToast);
end;

Solution

  • After much debugging I finally was successful getting the Toast Notification to display a Title and a message. It turns out I had to use a modified version of Remy's answer in combination with some of the Embaracdero's original demo code to get it to work.

    Thank you Remy! I did not think I was going to get there but I finally did. To save others who are trying to do this a lot of grief my working code is shown below:

    procedure TForm1.ShowToast(const AMessage: String; const ATitle: String = '');
    { Send a Toast Notification }
    var
      LINotificationManagerStatics: IToastNotificationManagerStatics;
      LToast: IToastNotification;
      LToastFactory: IToastNotificationFactory;
      LToastNotifier: IToastNotifier;
      LClassId: HString;
      LAccepted: TAcceptedEventHandler;
      LXMLTemplate: Xml_Dom_IXmlDocument;
      iTextNode: Xml_Dom_IXmlNode;
      LTextNodeList: Xml_Dom_IXmlNodeList;
      LTagName: HString;
      LTitle: HString;
      LMessage: HString;
    
      function GetActivationFactory(const ClassId: String; const Iid: String)
        : IInspectable;
      begin
        if Succeeded(WindowsCreateString(PWideChar(ClassId), Length(ClassId),
          LClassId)) then
          OleCheck(RoGetActivationFactory(LClassId, TGUID.Create(Iid), Result));
      end;
    
    begin
      LINotificationManagerStatics := GetActivationFactory
        (SToastNotificationManager, '{50AC103F-D235-4598-BBEF-98FE4D1A3AD4}')
        as IToastNotificationManagerStatics;
      if Succeeded(WindowsCreateString(PWideChar(NotificationTitle1.Text),
        Length(NotificationTitle1.Text), LClassId)) then
        LToastNotifier := LINotificationManagerStatics.CreateToastNotifier
          (LClassId);
      LXMLTemplate := LINotificationManagerStatics.GetTemplateContent
        (ToastTemplateType.ToastText02);
      if Succeeded(WindowsCreateString(PWideChar('text'), Length('text'), LTagName))
      then
        LTextNodeList := LXMLTemplate.getElementsByTagName(LTagName);
      if ATitle <> '' then
      begin
        if Succeeded(WindowsCreateString(PWideChar(ATitle), Length(ATitle), LTitle))
        then
          LTextNodeList.Item(0).AppendChild(LXMLTemplate.CreateTextNode(LTitle)
            as Xml_Dom_IXmlNode);
        iTextNode := LTextNodeList.Item(1);
      end
      else
      begin
        iTextNode := LTextNodeList.Item(0);
      end;
      if Succeeded(WindowsCreateString(PWideChar(AMessage), Length(AMessage),
        LMessage)) then
        iTextNode.AppendChild(LXMLTemplate.CreateTextNode(LMessage)
          as Xml_Dom_IXmlNode);
      LToastFactory := GetActivationFactory(SToastNotification,
        '{04124B20-82C6-4229-B109-FD9ED4662B53}') as IToastNotificationFactory;
      LToast := LToastFactory.CreateToastNotification(LXMLTemplate);
      LAccepted := TAcceptedEventHandler.Create;
      LToast.add_Activated(LAccepted);
      LToastNotifier.Show(LToast);
    end;
    

    Usage:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowToast(NotificationMessage1.Text, NotificationTitle1.Text);
    end;