[c#] NotifyIcon - Detect MouseOut

Knossos
Knossos used Ask the Experts™
on
Hi :)

A very simple question, but one I cannot seem to find an answer to.

I need to make my Form show when the mouse goes over the NotifyIcon. It then needs to hide once the mouse has left the NotifyIcon.

I'm fairly sure I'm missing something simple, but the only method for that seems to be MouseMove. Which, as far as I can tell, has no property to determine if it has entered or left the icon area.

Thanks.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Author

Commented:
Those events do not exist in NotifyIcon.
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
There is NO built-in API at the operating system level for EASILY asking the location of an icon in the system tray.

There are several "solutions" out there that involve interrogating the tray based on the known messages for its class type but nothing that works rock solid for all possible conditions.  Additionally, this approach relies on knowing the "ID" of the icon beforehand...something that you only have when you create icons manually via the WinAPIs (not using the .Net NotifyIcon).  Other approaches include attempting to find an image match in the tray or drawing your icon temporarily as a solid color that can be more easily detected.
See: http://www.codeproject.com/KB/shell/ctrayiconposition.aspx
High School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009
Commented:
Would it be enough to know when the mouse has the left the TRAY AREA instead?

In VB.Net:
Imports System.Runtime.InteropServices
Public Class Form1
 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Timer1.Interval = 100
        Timer1.Enabled = False
    End Sub
 
    Private Sub NotifyIcon1_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles NotifyIcon1.MouseMove
        Timer1.Start()
    End Sub
 
    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        If Not WinAPI.GetTrayRectangle.Contains(Cursor.Position) Then
            Timer1.Stop()
            MessageBox.Show("Left the Entire Tray Area!")
        End If
    End Sub
 
End Class
 
Public Class WinAPI
 
    Public Structure RECT
 
        Public Left As Integer
        Public Top As Integer
        Public Right As Integer
        Public Bottom As Integer
 
        Public Overrides Function ToString() As String
            Return "(" & Left & ", " & Top & ") --> (" & Right & ", " & Bottom & ")"
        End Function
 
    End Structure
 
    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Public Shared Function FindWindow( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As IntPtr
    End Function
 
    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Public Shared Function FindWindowEx( _
        ByVal hwndParent As IntPtr, _
        ByVal hwndChildAfter As IntPtr, _
        ByVal lpszClass As String, _
        ByVal lpszWindow As String) As IntPtr
    End Function
 
    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Public Shared Function GetWindowRect( _
        ByVal hwnd As IntPtr, _
        ByRef lpRect As WinAPI.RECT) As Integer
    End Function
 
    Public Shared Function GetTrayHandle() As IntPtr
        Dim TaskBarHandle As IntPtr = WinAPI.FindWindow("Shell_TrayWnd", Nothing)
        If Not TaskBarHandle.Equals(IntPtr.Zero) Then
            Return WinAPI.FindWindowEx(TaskBarHandle, Nothing, "TrayNotifyWnd", Nothing)
        End If
        Return IntPtr.Zero
    End Function
 
    Public Shared Function GetTrayRectangle() As Rectangle
        Dim RC As WinAPI.RECT
        WinAPI.GetWindowRect(WinAPI.GetTrayHandle, RC)
        Return New Rectangle(New Point(RC.Left, RC.Top), New Size((RC.Right - RC.Left) + 1, (RC.Bottom - RC.Top) + 1))
    End Function
 
End Class

Open in new window

Author

Commented:
That works great :)

I converted it to C# and setup Delegates and it works like a charm now.

I've attached as a snippet for anyone who sees this in the future.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
 
namespace OgameChecker.Library
{
    class TrayHelper
    {
        Timer trayTimer;
        public delegate void MouseInDelegate(object sender, MouseEventArgs e);
        public delegate void MouseOutDelegate();
        public MouseInDelegate mouseInDelegate;
        public MouseOutDelegate mouseOutDelegate;
 
        public TrayHelper(NotifyIcon icon)
        {
            trayTimer = new Timer();
            trayTimer.Interval = 100;
            trayTimer.Enabled = false;
 
            icon.MouseMove += new MouseEventHandler(icon_MouseMove);
            trayTimer.Tick += new EventHandler(trayTimer_Tick);
        }
 
        void icon_MouseMove(object sender, MouseEventArgs e)
        {
 	        trayTimer.Start();
            mouseInDelegate(sender, e);
        }
 
        void trayTimer_Tick(object sender, EventArgs e)
        {
 	        if(!WinAPI.GetTrayRectangle().Contains(Cursor.Position))
            {
                trayTimer.Stop();
                mouseOutDelegate();
            }
        }
    }
}
 
public class WinAPI
{
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
 
        public override string ToString()
        {
            return "(" + left + ", " + top + ") --> (" + right + ", " + bottom + ")";
        }
    }
 
    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    public static extern IntPtr FindWindow(string strClassName, string strWindowName);
    
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className,  IntPtr windowTitle);
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
 
 
    public static IntPtr GetTrayHandle()
    {
        IntPtr taskBarHandle = WinAPI.FindWindow("Shell_TrayWnd", null);
        if(!taskBarHandle.Equals(IntPtr.Zero))
        {
            return WinAPI.FindWindowEx(taskBarHandle, IntPtr.Zero, "TrayNotifyWnd", IntPtr.Zero);
        }
        return IntPtr.Zero;
    }
 
    public static Rectangle GetTrayRectangle()
    {
        WinAPI.RECT rect;
        WinAPI.GetWindowRect(WinAPI.GetTrayHandle(), out rect);
        return new Rectangle(new Point(rect.left, rect.top), new Size((rect.right - rect.left) + 1, (rect.bottom - rect.top) + 1));
    }
}

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial