如何在 C# Windows 应用程序中使用自定义图像和 onclick 函数发出通知?

How can I make a notification in a C# Windows app with a custom image and onclick function?

我的最终目标是在 C#.NET 中创建一个 Windows 10 应用程序,向用户显示通知。通知应该有标题、描述、至少一张图片,点击后会打开一个网页。如果用户没有点击它,它也应该存储在通知管理器中(或者 Windows 中列出通知的任何地方)。这就是目标。

我已经尝试了很多不同的方法来做到这一点,但无法使任何一种方法正常工作。

我当前的代码使用 Microsoft.Toolkit.Uwp.Notifications NuGet 包,主要取自此示例: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=builder-syntax

就目前而言,以下代码运行良好:

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();
        }
    }
}

这是它产生的通知...


第一个问题

第一个问题是我无法添加自定义图像,尽管据说可以通过三种不同的方法添加图像。我第一次尝试这个:

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

成功去除通知左侧的默认图标,并在底部添加应用的名称(因为想法是徽标已被删除,所以应用必须通过一些来识别其他方法)。然而,没有新的图像代替旧的。只是空白 space.

我也试过这个:

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

但据我所知,这与第一个版本几乎没有任何变化。

最后我尝试了这个:

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

这似乎在描述下方添加了一些空白 space,但没有图像。


第二个问题

另一个问题是我不知道如何通过这个过程添加一个完整的 onclick 动作。我对需要单击的按钮或通知本身的单击操作都非常满意。但不管怎么用,最终还是需要在用户的默认浏览器中打开一个指定的URL。


其他尝试

我还玩过其他发送通知的进程,比如 ShowBalloonTip 进程。对于自定义图像,这似乎根本没有选项,这正是我想要的。但是,我可以 select 指定图像列表中的图像,包括我在此代码中选择的“警告”图标,并且添加 onclick 操作很简单:

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);
        }
    }
}

我也尝试过使用 ToastNotificationManager,但我得到的结果与我使用 Microsoft.Toolkit.Uwp.Notifications 得到的结果相同......除了 ToastNotificationManager 需要 AppID 工作,我很难弄清楚我应该如何为我的小 Visual Studio 测试应用程序创建这样的东西。

无论如何,如果你能指出正确的方向来帮助我实现我的目标(最好是 minimal, reproducible example!),我将不胜感激!

在评论中提供的 link @Fildor 的帮助下,以及对如何处理本地图像的一些创造性思考,我终于有了一个可行的解决方案。这是 link: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast?tabs=desktop

我最终将我需要的两个图像(都将与应用程序一起部署)转换为数据 URI,就像人们在 HTML 中可能使用的那样。然后我将它们保存在本地。然后我在 AddAppLogoOverride 方法中使用 C# Uri 对象。可能有更简单的方法,但这是我能想到的最好的方法。

我修改后的工作(即 - 如果您使用上传到 opinionatedgeek 编码器的真实图像中的真实图像数据,则为“工作”)示例代码如下。

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://docs.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);
                // 
                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)
        {
            // 
            var binData = Convert.FromBase64String(dataUrl);
            System.IO.File.WriteAllBytes(savePath, binData);
            return savePath;
        }

        private void SaveImageFilesToCommonFolder()
        {
            // 
            // 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);
        }
    }
}

如果您不需要从网络上获取图像,而只需在您的项目中引用文件,文档就不够清楚,但是当您使用常规 desktop/console 应用程序时您不受与 UWP 应用相同的限制。

您可以将文件:/// URL 直接用于您项目中的图像,它适用于应用程序图标和“英雄图像”:

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();

我达到这个 post 因为我遇到了完全相同的问题。 这很有趣,因为根据您的应用程序名称,我认为我们正在构建相同的应用程序 :) 无论如何,我找到了一种更简单的方法来实现这一目标。

首先,在您的项目上创建一个目录并将图像放在那里。就我而言,我创建了一个“图像”目录:

确保图像文件的“Build Action”设置为“Content”并且您的“”复制到输出目录”设置为“如果较新则复制”(或“始终”)。

那么你的代码可以是这样的:

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();

结果:

可能有点晚了,但我对吐司有同样的问题,如果用户关闭了通知,不确定它是否有帮助。

为了表达祝酒词,我试了一下成功了:Tulpep.NotificationWindow

效果很好,您添加一个控件并访问所有参数,并且能够显示图像(并调整大小)。

下面是 link 的示例:https://www.c-sharpcorner.com/article/working-with-popup-notification-in-windows-forms/