8

My ultimate goal is to have a Windows 10 app in C#.NET that displays a notification to the user. The notification should have a title, description, at least one image, and when clicked, it should open a webpage. It should also be stored in notification manager (or whatever that place in Windows that lists notifications is called) if the user doesn't click on it. So that's the goal.

I've tried a bunch of different ways of doing this, and cannot get any of them to work properly.

My current code uses the Microsoft.Toolkit.Uwp.Notifications NuGet package and is primarily taken from this sample: https://learn.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=builder-syntax

The following code works great, as far as it goes:

using System;
using System.Windows;
using Microsoft.Toolkit.Uwp.Notifications;

namespace PushClient
{
    public partial class App : System.Windows.Application
    {
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            new ToastContentBuilder()
            .AddText("My Title")
            .AddText("My Description")
            .Show();
        }
    }
}

Here is the notification it produces...

Just title and description


FIRST PROBLEM

The first problem is that I cannot add a custom image, even though there are supposedly three different methods through which an image can be added. I first tried this:

new ToastContentBuilder()
.AddText("My Title")
.AddText("My Description")
.AddAppLogoOverride((new Uri("https://picsum.photos/48?image=883")), ToastGenericAppLogoCrop.Circle)
.Show();

That successfully removes the default icon at the left of the notification and adds the name of the app at the bottom (since the idea is that the logo has been gotten rid of, so the app has to be identified through some other method). However, there is no new image in place of the old. Just blank space.

An empty icon area

I also attempted this:

new ToastContentBuilder()
.AddText("My Title")
.AddText("My Description")
.AddHeroImage(new Uri("https://picsum.photos/364/180?image=1043"))
.Show();

But that changed literally nothing from the first version as far as I could tell.

Same as the plain text version

Finally I tried this:

new ToastContentBuilder()
.AddText("My Title")
.AddText("My Description")
.AddInlineImage(new Uri("https://picsum.photos/360/202?image=1043"))
.Show();

That seems to have added some blank space beneath the description, but no image.

Blank space at foot


SECOND PROBLEM

Another problem is that I cannot figure out how to add a full onclick action through this process. I would be perfectly happy with either a button that needs to be clicked, or a click action on the notification itself. But however it works, it ultimately needs to open a specified URL in the user's default browser.


OTHER ATTEMPTS

I have also played with other processes of sending notifications, like the ShowBalloonTip process. This seems to have no option at all for a custom image, which is what I want. However, I can select an image from a specified list of images, including a "warning" icon, which I've chosen in this code, and the onclick action is simple to add:

using System;
using System.Threading;
using System.Windows;
using System.Windows.Forms;

namespace PushClient
{
    public partial class App : System.Windows.Application
    {
        private NotifyIcon _notifyIcon = new NotifyIcon();

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            try
            {
                _notifyIcon = new NotifyIcon();
                _notifyIcon.Icon = PushClient.Properties.Resources.accountable2you;
                _notifyIcon.Visible = true;
                _notifyIcon.ContextMenuStrip = new System.Windows.Forms.ContextMenuStrip();

                Thread t = new Thread(() => LaunchBalloonNotification(_notifyIcon, "My Title", "My Description"));
                t.Start();
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
        private static void notifyIcon_BalloonTipClicked(object sender, EventArgs e)
        {
            Console.WriteLine("Action clicked");
        }
        private static void LaunchBalloonNotification(NotifyIcon ico, String title, String msg)
        {
            ico.ShowBalloonTip(
                10000,
                title,
                msg,
                ToolTipIcon.Warning
            );
            ico.BalloonTipClicked += new EventHandler(notifyIcon_BalloonTipClicked);
        }
    }
}

No custom image

I have also tried working with ToastNotificationManager, but the results I get with that are identical to the results I get with Microsoft.Toolkit.Uwp.Notifications... except that ToastNotificationManager requires an AppID to work, and I've had difficulty figuring out how I'm properly supposed to create such a thing for my little Visual Studio test app.

Anyway, if you can point me in the right direction to help me achieve my goal here (preferably with a minimal, reproducible example!), it would be much appreciated!

gcdev
  • 1,406
  • 3
  • 17
  • 30
  • 1
    You did go through [this](https://learn.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast?tabs=desktop) , right? – Fildor Mar 18 '21 at 16:49
  • 1
    @Fildor I had not seen that tutorial, no! Thank you -- it was very helpful in terms of adding actions to buttons, and, from what it says, it looks like I may not be able to use http images. So I apparently need to figure out how to use local images, which... I also haven't been able to get to work. Sigh. I'll research that some more! Thanks for your help! – gcdev Mar 18 '21 at 19:43

5 Answers5

7

With help from a link @Fildor provided in comments, and with some creative thinking about how to handle local images, I finally have a working solution. Here is the link: https://learn.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast?tabs=desktop

I wound up converting the two images that I need, both of which were going to be deployed with the app, into data URIs like a person might use in HTML. Then I saved those locally. Then I use the C# Uri object in the AddAppLogoOverride method. There's probably an easier way, but this was the best I could come up with that actually worked.

My revised & working (that is -- "working" if you use real image data from a real image uploaded to the opinionatedgeek encoder) sample code is below.

using System;
using System.Windows;
using Microsoft.Toolkit.Uwp.Notifications;
using System.IO;

namespace PushClient
{
    public partial class App : System.Windows.Application
    {
        private String imageFilePath = String.Empty;

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            try
            {
                SaveImageFilesToCommonFolder();
                LaunchToastNotification("Hello World!", "This is such a nice world!", "https://presuppositions.org");
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        private void LaunchToastNotification(string title, string description, string url)
        {
            // https://learn.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast?tabs=desktop
            Uri img = new Uri(imageFilePath);

            // Listen to notification activation
            ToastNotificationManagerCompat.OnActivated += toastArgs =>
            {
                // Obtain the arguments from the notification
                ToastArguments args = ToastArguments.Parse(toastArgs.Argument);
                // https://stackoverflow.com/questions/4580263/how-to-open-in-default-browser-in-c-sharp
                System.Diagnostics.Process.Start(args["url"]);
            };

            new ToastContentBuilder()
                .AddText(title)
                .AddText(description)
                .AddAppLogoOverride(img)
                .AddButton(new ToastButton()
                    .SetContent("View Report")
                    .AddArgument("action", "viewReport")
                    .AddArgument("url", url))
                .Show();
        }

        private string SaveDataUrlToFile(string dataUrl, string savePath)
        {
            // https://stackoverflow.com/questions/27710576/convert-from-a-dataurl-to-an-image-in-c-sharp-and-write-a-file-with-the-bytes
            var binData = Convert.FromBase64String(dataUrl);
            System.IO.File.WriteAllBytes(savePath, binData);
            return savePath;
        }

        private void SaveImageFilesToCommonFolder()
        {
            // https://stackoverflow.com/questions/27710576/convert-from-a-dataurl-to-an-image-in-c-sharp-and-write-a-file-with-the-bytes
            // Write images to common app data folder
            var directory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            // Uploaded PNG to this location to get the Data URI,
            // which is really much longer than displayed here:
            // https://www.opinionatedgeek.com/codecs/base64encoder
            String imageFileData = "iVBORw0KGgoAAAANUgAAAMAAAADRU5ErkJggg==";
            imageFilePath = Path.Combine(directory, "myimage.png");
            SaveDataUrlToFile(imageFileData, imageFilePath);
        }
    }
}
gcdev
  • 1,406
  • 3
  • 17
  • 30
  • 1
    You may want to include the the link from the comment in your question. Comments _may_ be "cleaned up". While Link-only answers are low quality, it is totally ok to include references for further reading in answers that are useful without them. – Fildor Mar 19 '21 at 08:01
1

If you don't need to fetch images from the web and can just reference files within your project, the docs don't make this clear enough but when you're working with a regular desktop/console application you're not bound by the same restrictions as a UWP app would have.

You can make a file:/// URL directly to images located in your project and it will work for both app icons and "hero images":

var iconUri = "file:///" + Path.GetFullPath("images/icon.png");
var imageUri = "file:///" + Path.GetFullPath("images/image.jpg");

new ToastContentBuilder()
    .AddText("Notification text")
    .AddAppLogoOverride(new Uri(iconUri), ToastGenericAppLogoCrop.Circle)
    .AddHeroImage(new Uri(imageUri))
    .Show();

Julian Melville
  • 185
  • 2
  • 9
1

I reached this post because I had the exact same problem. It's funny because based on your application name, I think we are building the same app :) Anyway, I've found out an easier way of achieving this.

First thing, create a directory on your project and put your images there. In my case, I created an "Images" directory:

enter image description here

Make sure the "Build Action" for your image file is set to "Content" and your "Copy to Output Directory" is set to "Copy if newer" (or "Always").

Then your code could be simply something like this:

var imageUri = Path.GetFullPath(@"Images\logo.png");

new ToastContentBuilder()
    .AddArgument("action", "viewConversation")
    .AddArgument("conversationId", 9813)
    .AddText(title)
    .AddText(message + " URL:" + url)
    .AddAppLogoOverride(new Uri(imageUri))
    .AddHeroImage(new Uri(imageUri))
    .AddButton(new ToastButton()
        .SetContent("Open URL")
        .SetProtocolActivation(new Uri(url))
     )
    .Show();

The result:

enter image description here

Tiago
  • 321
  • 3
  • 4
0

I had the same problem and I solved it like this:

  • In solution explorer of VisualStudio I have created folder named "Icon" and inside this folder I put "pendrive.png" image.

  • Properties of the file are set to "Embedded Resource"(can be "Content" as well) and "Copy Always"(can be "Copy if newer" as well)

In the code:

Uri uri = new Uri("file:///" + Directory.GetCurrentDirectory() + "/Icon/pendrive.png");

In the ToastContentBuilder:

AddAppLogoOverride(uri, ToastGenericAppLogoCrop.Circle)
-1

maybe a bit late, but I had the same issue with the toast, if the user turned off the notification, not sure it's helpful.

To show toast like, I tried this with success : Tulpep.NotificationWindow

Works great, you add a control and access all the parameters, and able to show an image (and to resize).

Here's the link to an example : https://www.c-sharpcorner.com/article/working-with-popup-notification-in-windows-forms/