In the Deactivate
event, you can get the Rectangle
of the NotifyIcon
's Icon
, then check if it Contains()
the MousePosition
, and then decide whether to Close()
the MainForm
.
I referenced this post: How to obtain the icon rectangle of a NotifyIcon
internal static class NotifyIconExtensions
{
//Works with Shell32.dll (version 6.1 or later)
[DllImport("shell32.dll")]
static extern int Shell_NotifyIconGetRect([In] ref NOTIFYICONIDENTIFIER identifier, [Out] out RECT iconLocation);
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct NOTIFYICONIDENTIFIER
{
public uint cbSize;
public IntPtr hWnd;
public uint uID;
public Guid guidItem;
}
public static bool TryGetRectangle(this NotifyIcon notifyIcon, out Rectangle rectangle)
{
rectangle = Rectangle.Empty;
var type = notifyIcon.GetType();
var idFieldName = "id";
var windowFieldName = "window";
// Due to the difference in field names between .NET and .NET Framework,
// it requires conditional checking.
var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
if (!fields.Any(x => x.Name == idFieldName))
{
idFieldName = "_id";
windowFieldName = "_window";
}
var idFieldInfo = type.GetField(idFieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (idFieldInfo == null)
return false;
var iconId = (uint)idFieldInfo.GetValue(notifyIcon);
var windowFieldInfo = type.GetField(windowFieldName, BindingFlags.NonPublic | BindingFlags.Instance);
var nativeWindow = windowFieldInfo.GetValue(notifyIcon) as NativeWindow;
if (nativeWindow == null)
return false;
var iconHandle = nativeWindow.Handle;
var nid = new NOTIFYICONIDENTIFIER();
nid.cbSize = (uint)Marshal.SizeOf(nid);
nid.hWnd = iconHandle;
nid.uID = iconId;
int result = Shell_NotifyIconGetRect(ref nid, out var rect);
if (result != 0)
return false;
rectangle = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
return true;
}
}
// in MainForm
protected override void OnDeactivate(EventArgs e)
{
if (Program.notifyIcon.TryGetRectangle(out Rectangle rect))
{
Debug.WriteLine("{0}, {1}", MousePosition, rect);
if (rect.Contains(MousePosition))
return;
}
Close();
}
This will raise another issue: after the user clicks the NotifyIcon
and triggers the Deactivate
event of MainForm
, if they click on another window, the Deactivate
event of MainForm
will not be triggered again.