how to show Balloon tip like Windows 10 Balloon tip without stretching icon
Asked Answered
C

1

10

I have WPF desktop application which is pushing some notifications using:

NotifyIcon.ShowBalloonTip(,,,ToolTipIcon.None).

The problem is:

Windows 10 uses new "Windows 10 rectangle with image and text" style for Balloon notifications (I don't know how exactly it is called).

If I use ToolTipIcon.None parameter, it gets my application icon which was set to NotifyIcon.Icon property and shows it in this Balloon notification. And this icon is blurred/stretched (like too small icon was taken and stretched to size which is needed for this ballon image).

My ico-file contains several sizes: 16*16, 32*32, 128*128, 256*256 etc. I've already tried to set icon file with only one 128*128 size, but it didn't work.

Should it work at all?

Thank you.

Corell answered 13/1, 2016 at 7:37 Comment(3)
you know it is possible that this is not possibleProtoxylem
msdn.microsoft.com/en-us/library/windows/desktop/… shows a beautiful custom icon so it is possibleProtoxylem
For a reference of the problem - see #35242900Protoxylem
P
2

The following class should display a smooth, large icon in the balloon tip on Windows 10. It's by no means polished, but it should prove the concept. A good portion of the code is copied directly from the decompiled Microsoft NotifyIcon class.

The key changes between the original NotifyIcon class and this are:

  1. The addition (and use) of the NOTIFYICONDATA.hBalloonIcon member.
  2. The setting of nOTIFYICONDATA.dwInfoFlags = NIIF_LARGE_ICON | NIIF_USER, instead of NIIF_NONE.

You can read the specifics of the NOTIFYICONDATA structure here, but the gist is:

  • hBalloonIcon was added in Vista, specifically for use outside of the system tray.
  • NIIF_USER says to use the icon in hBalloonIcon as the balloon icon.
  • NIIF_LARGE_ICON says that the balloon's icon should be large.

NotifyIconLarge class:

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

namespace BalloonNotification
{
    public class NotifyIconLarge : IDisposable
    {
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        public static extern int Shell_NotifyIcon(int message, NOTIFYICONDATA pnid);

        [DllImport("Comctl32.dll", CharSet = CharSet.Unicode)]
        private static extern IntPtr LoadIconWithScaleDown(IntPtr hinst, string pszName, int cx, int cy, out IntPtr phico);

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool DestroyIcon(IntPtr hIcon);

        private const int NIIF_LARGE_ICON = 0x00000020;
        private const int NIIF_USER = 0x00000004;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public class NOTIFYICONDATA
        {
            public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
            public IntPtr hWnd;
            public int uID;
            public int uFlags;
            public int uCallbackMessage;
            public IntPtr hIcon;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string szTip;
            public int dwState;
            public int dwStateMask;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string szInfo;
            public int uTimeoutOrVersion;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
            public string szInfoTitle;
            public int dwInfoFlags;
            Guid guidItem;
            public IntPtr hBalloonIcon;
        }

        private IntPtr _windowHandle;
        private IntPtr _hIcon;
        private bool _added;
        private int _id = 1;
        private string _tipText;

        public NotifyIconLarge(IntPtr windowHandle, string iconFile, int iconSize, string tipText)
        {
            _windowHandle = windowHandle;
            _tipText = tipText;
            IntPtr result = LoadIconWithScaleDown(IntPtr.Zero, iconFile, iconSize, iconSize, out _hIcon);
            UpdateIcon(true);
        }

        private void UpdateIcon(bool showIconInTray)
        {
            NOTIFYICONDATA nOTIFYICONDATA = new NOTIFYICONDATA();
            nOTIFYICONDATA.uCallbackMessage = 2048;
            nOTIFYICONDATA.uFlags = 1;
            nOTIFYICONDATA.hWnd = _windowHandle;
            nOTIFYICONDATA.uID = _id;
            nOTIFYICONDATA.hIcon = IntPtr.Zero;
            nOTIFYICONDATA.szTip = null;
            if (_hIcon != IntPtr.Zero)
            {
                nOTIFYICONDATA.uFlags |= 2;
                nOTIFYICONDATA.hIcon = _hIcon;
            }
            nOTIFYICONDATA.uFlags |= 4;
            nOTIFYICONDATA.szTip = _tipText;
            nOTIFYICONDATA.hBalloonIcon = _hIcon;
            if (showIconInTray && _hIcon != IntPtr.Zero)
            {
                if (!_added)
                {
                    Shell_NotifyIcon(0, nOTIFYICONDATA);
                    _added = true;
                }
                else
                {
                    Shell_NotifyIcon(1, nOTIFYICONDATA);
                }
            }
            else
            {
                if (_added)
                {
                    Shell_NotifyIcon(2, nOTIFYICONDATA);
                    _added = false;
                }
            }
        }

        public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
        {
            NOTIFYICONDATA nOTIFYICONDATA = new NOTIFYICONDATA();
            nOTIFYICONDATA.hWnd = _windowHandle;
            nOTIFYICONDATA.uID = _id;
            nOTIFYICONDATA.uFlags = 16;
            nOTIFYICONDATA.uTimeoutOrVersion = timeout;
            nOTIFYICONDATA.szInfoTitle = tipTitle;
            nOTIFYICONDATA.szInfo = tipText;
            switch (tipIcon)
            {
                case ToolTipIcon.None:
                    nOTIFYICONDATA.dwInfoFlags = NIIF_LARGE_ICON | NIIF_USER;
                    break;
                case ToolTipIcon.Info:
                    nOTIFYICONDATA.dwInfoFlags = 1;
                    break;
                case ToolTipIcon.Warning:
                    nOTIFYICONDATA.dwInfoFlags = 2;
                    break;
                case ToolTipIcon.Error:
                    nOTIFYICONDATA.dwInfoFlags = 3;
                    break;
            }
            int ret = Shell_NotifyIcon(1, nOTIFYICONDATA);
        }

        public void RemoveFromTray()
        {
            UpdateIcon(false);
            if (_hIcon != IntPtr.Zero)
                DestroyIcon(_hIcon);
        }

        ~NotifyIconLarge()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Dispose(bool disposing)
        {
            RemoveFromTray();
        }
    }
}

Declare it somewhere:

private NotifyIconLarge _nil;

Then use it like this:

string fileName = @"C:\path_to_some_icon.ico";
_nil = new NotifyIconLarge(Handle, fileName, 64, "Icon Tip");
_nil.ShowBalloonTip(10000, "Balloon Title", "Balloon Text", ToolTipIcon.None);

When finished, remove the tray icon:

_nil.RemoveFromTray();
Postulate answered 5/3, 2016 at 6:29 Comment(1)
Sweet - was just dreading about this approach - will check it.Protoxylem

© 2022 - 2024 — McMap. All rights reserved.