1

I'm using System.Windows.Forms.NotifyIcon in WPF app, I need to show Hyperlink in System.Windows.Forms.NotifyIcon, I tried to set the Hyperlink in BalloonTipText property, but NotifyIcon not recognize the Hyperlink:

NotifyIcon notifyIcon = new NotifyIcon();
notifyIcon.BalloonTipTitle = "BalloonTipTitle";
notifyIcon.BalloonTipText = "Click <a href='http://somewebsite.com'>here</a> to download.";
...
notifyIcon.Visible = true;                            
notifyIcon.ShowBalloonTip(3000);

enter image description here

In this question exist a solution with a TextBlock in WPF: Add hyperlink to textblock wpf, but NotifyIcon is a Windows Forms component with limited properties. Is it possible to show Hyperlink in System.Windows.Forms.NotifyIcon?

Update: Something just like this:

enter image description here

Ejrr1085
  • 975
  • 2
  • 16
  • 29

2 Answers2

1

If you are targeting >=Windows10, then you should use the newer Windows.UI.Notifications API: Notifications Overview, Adding buttons and inputs. If you don't like to edit pure XML to configure the toast, you should also install the Microsoft.Toolkit.Uwp.Notifications NuGet package. Using this API requires your project to target Windows 10 (configure in project properties page - the smallest Windows 10 version is sufficient).

The old Windows Forms API is not that versatile. It only shows a clickable popup with non-interactive content. I'm not sure System.Windows.Forms.NotifyIcon is even supported on Windows 10 and later. You should verify this, in case your development environment runs an older Windows version. However, if you need interactive toasts or adaptive tiles, you should use the new API mentioned above.

If you decide to stick to the Windows Forms API, your only option is to attach a general click handler to start navigation on click:

private void OnMainWIndowLoaded(object sender, EventArgs e)
{
  // Handle toast click
  notifyIcon.BalloonTipClicked += NavigateLinkOnToastClicked;

  // Handle icon click
  notifyIcon.Click += NavigateLinkOnToastClicked;
}

private void NavigateLinkOnToastClicked(object sender, EventArgs e)
  => Process.Start("explorer", "https://stackoverflow.com/q/72244095/3141792");
BionicCode
  • 1
  • 4
  • 28
  • 44
  • Thanks for you answer, but I'm targeting since Windows 7, how I could show as clicked hyperlink? – Ejrr1085 May 16 '22 at 20:48
  • As I stated in my answer, all you can do is to handle the toast's click event. prior to Windows 10 you can't create interactive notification toasts. You can only display plain text and an icon. See the example code in order to make the toast clickable. – BionicCode May 16 '22 at 22:43
0

NotifyIcon.BalloonTipText is plain string and so it is not possible. In addition, since NotifyIcon is WinForms component, you cannot use WPF components directly from it.

You will have some options.

Option 1: Use Hardcodet NotifyIcon for WPF.

Option 2: Implement a mechanism to open a WPF window when NotifyIcon is clicked. For example, create a class to hold and show NofityIcon as follows.

// using System;
// using System.Runtime.InteropServices;
// using System.Windows;
// using System.Windows.Forms;

public class NotifyIconHolder
{
    public event EventHandler<Point>? MouseRightClick;
    public event EventHandler<Point>? MouseLeftClick;
    public event EventHandler<Point>? MouseDoubleClick;

    [DllImport("User32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetCursorPos(out POINT lpPoint);

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;

        public static implicit operator System.Windows.Point(POINT p) => new(p.x, p.y);
    }

    private NotifyIcon? _notifyIcon;

    public void ShowIcon(System.Drawing.Icon icon, string text)
    {
        if (_notifyIcon is null)
        {
            _notifyIcon = new NotifyIcon()
            {
                Icon = icon,
                Text = text
            };
            _notifyIcon.MouseClick += (_, e) =>
            {
                if (GetCursorPos(out POINT position))
                {
                    switch (e.Button)
                    {
                        case MouseButtons.Left:
                            MouseLeftClick?.Invoke(null, position);
                            break;
                        case MouseButtons.Right:
                            MouseRightClick?.Invoke(null, position);
                            break;
                    }
                }
            };
            _notifyIcon.MouseDoubleClick += (_, _) =>
            {
                if (GetCursorPos(out POINT position))
                {
                    MouseDoubleClick?.Invoke(null, position);
                }
            };
            _notifyIcon.Visible = true;
        }
    }

    public void HideIcon()
    {
        _notifyIcon?.Dispose();
        _notifyIcon = null;
    }
}

Its usage. You will need to think about where to show a WPF window but it's another issue.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += OnLoaded;
    }

    private NotifyIconHolder? _holder;

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        _holder = new NotifyIconHolder();
        _holder.MouseLeftClick += OnNotifyIconLeftClick;
        _holder.MouseRightClick += OnNotifyIconRightClick;

        // Assuming application's resources include an Icon (.ico).
        _holder.ShowIcon(Properties.Resources.Icon, "Sample");
    }

    private void OnNotifyIconLeftClick(object? sender, Point e)
    {
        // Show the WPF window for left click.
    }

    private void OnNotifyIconRightClick(object? sender, Point e)
    {
        // Show the WPF window for right click.
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        _holder?.HideIcon();
    }
}
emoacht
  • 2,764
  • 1
  • 13
  • 24