Skip to content

Instantly share code, notes, and snippets.

@KOZ60
Last active May 20, 2024 02:44
Show Gist options
  • Save KOZ60/a62ff318ec3dbf128953af0be1c04a2d to your computer and use it in GitHub Desktop.
Save KOZ60/a62ff318ec3dbf128953af0be1c04a2d to your computer and use it in GitHub Desktop.
NotifyIcon Sample
using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public partial class Form1 : Form
{
private NotifyIcon notifyIcon;
public Form1() {
InitializeComponent();
ControlBox = false;
ShowInTaskbar = false;
Text = "NotifyIcon Demo";
InitializeNotifyIcon();
}
private void InitializeNotifyIcon() {
notifyIcon = new NotifyIcon();
notifyIcon.Icon = SystemIcons.Information;
notifyIcon.Text = "NotifyIcon Demo";
notifyIcon.Visible = true;
notifyIcon.ContextMenu = CreateContextMenu();
notifyIcon.MouseClick += NotifyIcon_MouseClick;
}
private ContextMenu CreateContextMenu() {
var exitMenuItem = new MenuItem("Exit");
exitMenuItem.Click += (s, e) => {
Application.Exit();
};
var menu = new ContextMenu();
menu.MenuItems.Add(exitMenuItem);
return menu;
}
protected override void SetVisibleCore(bool value) {
if (!IsHandleCreated) CreateHandle();
// Hide form on startup (Load event is not fired)
base.SetVisibleCore(false);
}
private const int WM_ACTIVATEAPP = 0x001C;
protected override void WndProc(ref Message m) {
// Hide Form when disactivate
if (m.Msg == WM_ACTIVATEAPP && m.WParam == IntPtr.Zero) {
base.SetVisibleCore(false);
}
base.WndProc(ref m);
}
private void NotifyIcon_MouseClick(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
Rectangle iconRectangle = GetIconRectangle(notifyIcon);
Screen screen = Screen.FromRectangle(iconRectangle);
Rectangle workingArea = screen.WorkingArea;
Rectangle dwmBounds = GetDwmRectangle(this);
Size offset = (Size)dwmBounds.Location - (Size)Location;
// It's difficult to display the taskbar anywhere other than at the bottom in Windows 11
// so I'll leave it up to you.
switch (GetTaskBarPosision()) {
case TaskBarPosision.Left:
break;
case TaskBarPosision.Right:
break;
case TaskBarPosision.Top:
break;
case TaskBarPosision.Bottom:
Left = workingArea.Right - dwmBounds.Width - offset.Width;
Top = iconRectangle.Top - dwmBounds.Height + offset.Height;
break;
}
base.SetVisibleCore(true); // Show Form
Activate();
}
}
private static Rectangle GetIconRectangle(NotifyIcon notifyIcon) {
var id = GetFieldValue<int>(notifyIcon, "id");
var window = GetFieldValue<NativeWindow>(notifyIcon, "window");
var identifier = new NOTIFYICONIDENTIFIER {
cbSize = Marshal.SizeOf(typeof(NOTIFYICONIDENTIFIER)),
hWnd = window.Handle,
uID = id,
guidItem = Guid.Empty
};
Shell_NotifyIconGetRect(ref identifier, out RECT rc);
return rc.Rectangle;
}
private static TValue GetFieldValue<TValue>(object target, string fieldName) {
var fi = target.GetType().GetField(fieldName,
BindingFlags.NonPublic | BindingFlags.Instance);
return (TValue)fi.GetValue(target);
}
public static Rectangle GetDwmRectangle(Form form) {
IntPtr hwnd = form.Handle;
int hResult;
// non-client rendering is enabled?
hResult = DwmGetWindowAttribute(hwnd,
DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_ENABLED, out bool enablled,
Marshal.SizeOf(typeof(bool)));
if (hResult != 0) throw new Win32Exception();
if (enablled) {
// Retrieves the extended frame bounds rectangle in screen space.
hResult = DwmGetWindowAttribute(hwnd,
DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out RECT rc,
Marshal.SizeOf(typeof(RECT)));
if (hResult != 0) throw new Win32Exception();
return rc.Rectangle;
} else {
return form.Bounds;
}
}
[DllImport("dwmapi.dll", SetLastError = true)]
private static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out RECT rect, int cbAttribute);
[DllImport("dwmapi.dll", SetLastError = true)]
private static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out bool enabled, int cbAttribute);
private enum DWMWINDOWATTRIBUTE
{
DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS,
DWMWA_HAS_ICONIC_BITMAP,
DWMWA_DISALLOW_PEEK,
DWMWA_EXCLUDED_FROM_PEEK,
DWMWA_CLOAK,
DWMWA_CLOAKED,
DWMWA_FREEZE_REPRESENTATION,
DWMWA_LAST
};
public static TaskBarPosision GetTaskBarPosision() {
var abd = new APPBARDATA();
if (SHAppBarMessage(ABM_GETTASKBARPOS, abd) != 0) {
return abd.uEdge;
} else {
throw new Win32Exception();
}
}
private const int ABM_GETTASKBARPOS = 0x00000005;
public enum TaskBarPosision
{
Left,
Top,
Right,
Bottom
}
[DllImport("shell32.dll", SetLastError = true)]
private static extern uint SHAppBarMessage(int dwMessage, APPBARDATA pData);
[StructLayout(LayoutKind.Sequential)]
private class APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public TaskBarPosision uEdge;
public RECT rc;
public int lParam;
public APPBARDATA() {
cbSize = Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public Rectangle Rectangle {
get {
return Rectangle.FromLTRB(Left, Top, Right, Bottom);
}
}
}
[StructLayout(LayoutKind.Sequential)]
private struct NOTIFYICONIDENTIFIER
{
public int cbSize;
public IntPtr hWnd;
public int uID;
public Guid guidItem;
}
[DllImport("shell32.dll")]
private static extern int Shell_NotifyIconGetRect(
ref NOTIFYICONIDENTIFIER identifier, out RECT iconLocation);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment