Home > Software engineering >  Is it possible to show Hyperlink in System.Windows.Forms.NotifyIcon?
Is it possible to show Hyperlink in System.Windows.Forms.NotifyIcon?

Time:05-16

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?

CodePudding user response:

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

CodePudding user response:

Since NotifyIcon is WinForms component, you cannot use WPF components directly from it.

You will have some options.

Option 1: Use System.Windows.Forms.ContenxtMenuStrip. It will give you the ability to customize the items to be shown.

Option 2: Use Hardcodet NotifyIcon for WPF.

Option 3: 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  = onl oaded;
    }

    private NotifyIconHolder? _holder;

    private void onl oaded(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();
    }
}
  • Related