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...
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.
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.
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.
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);
}
}
}
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!
Following are the three types of android notifications, Toast Notification – Shows message that fades away after a few seconds. (Background type also) Status Notification – Shows notification message and displayed till user action.
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);
}
}
}
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:
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:
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With