There are two mouse-messages that are never received unless you explicitly instruct Windows to track the mouse movement. The first message is WM_MOUSELEAVE that is supposed to report that the mouse has left the client-area. The second is WM_MOUSEHOVER which is posted after hovering a certain amount of time over some area. To obtain one (or both) of these messages you need to call TrackMouseEvent() API which notifies the application when the mouse leaves the window or when the mouse hovers over an area for a while.
The next program illustrates how to implement a mouseover feature by drawing a box that turns black when you move the mouse over it. The basic idea is to use WM_ MOUSEMOVE to know when the mouse has moved in or out of the box. The only problem is that if the user moves the mouse quickly outside the window, you won't get a WM_ MOUSEMOVE. To implement a correct behavior of mouseover, you need to know when the mouse has left the window entirely.
The program doesn’t use the _MouseMove eventsub, but combines the mouseover-logic into the _Message eventsub, which receives all (posted) mouse messages. There are no event subs for WM_MOUSELEAVE and WM_MOUSEHOVER, so they have to be handled in a general event- sub. An alternative would be to handle the messages in _MessageProc, but its use is a bit more complicated. In addition, _Message doesn’t require any return values, so it serves our purpose best.
OpenW Center 1 Do Sleep Until Win_1 Is Nothing Sub Win_1_Paint Box 10, 10, 100, 100 EndSub Sub Win_1_Message(hWnd%, Mess%, wParam%, lParam%) Static Bool fTrackingMouse, fBoxHighLighted Dim tme As TRACKMOUSEEVENT Local Int mx, my Switch Mess% Case WM_MOUSEMOVE ' Track a mouseleave event. Results in a WM_MOUSELEAVE ' message when the mouse leaves the window. If !fTrackingMouse ' set it only once tme.cbSize = SizeOf(TRACKMOUSEEVENT) tme.dwFlags = TME_LEAVE tme.hwndTrack = Me.hWnd fTrackingMouse = TrackMouseEvent(tme) != 0 EndIf ' If mouse is over the box start timer mx = LoWord(lParam%), my = HiWord(lParam%) If mx > 10 && mx < 100 && my > 10 && my < 100 tme.cbSize = SizeOf(TRACKMOUSEEVENT) tme.dwFlags = TME_HOVER ' start timer tme.hwndTrack = Me.hWnd tme.dwHoverTime = HOVER_DEFAULT ' use default time TrackMouseEvent(tme) ' now wait for WM_MOUSEHOVER Else If fBoxHighLighted ' hilighted and not over box Win_1.Invalidate 10, 10, 90, 90 fBoxHighLighted = False EndIf Case WM_MOUSELEAVE ' triggered by TrackMouseEvent fTrackingMouse = False ' TrackMouseEvent not active anymore If fBoxHighLighted ' redraw original box Win_1.Invalidate 10, 10, 90, 90 fBoxHighLighted = False ' box is not highlighted EndIf Case WM_MOUSEHOVER ' triggered by TrackMouseEvent's timer mx = LoWord(lParam%), my = HiWord(lParam%) ' mouse coordinates If !fBoxHighLighted && mx > 10 && mx < 100 && my > 10 && my < 100 PBox 10, 10, 100, 100 ' highlight the box fBoxHighLighted = True EndIf EndSwitch EndSub Public Const HOVER_DEFAULT = 0xFFFFFFFF Type TRACKMOUSEEVENT - DWord cbSize - DWord dwFlags - Handle hwndTrack - DWord dwHoverTime EndType Declare Function TrackMouseEvent Lib "user32" Alias _ "TrackMouseEvent" (ByRef EventTrack As TRACKMOUSEEVENT) As Long
The _Message sub declares two static booleans, fTrackingMouse and fBoxHighLighted, that keep track of the current state of the mouseover-logic. (If I would have used the _MouseMove eventsub to initiate the mouse tracking the variables should have been declared global, I always try to avoid global variables as much as possible.)
When the first (of many) WM_MOUSEMOVE message is received, the TrackMouseEvent() API is used to set up a WM_MOUSELEAVE "one-shot" event. Exactly one and only one WM_MOUSELEAVE message will be posted to the window specified in the hwndTrack member of the TRACKMOUSEEVENT structure, when the mouse has left the client area.
Note - The message will be generated only once. The application must call the TrackMouseEvent API again in order for the system to generate another WM_MOUSELEAVE message. In addition, when the mouse pointer is not over the application, a call to TrackMouseEvent() will result in the immediate posting of a WM_MOUSELEAVE message.
When the mouse is over the box, the TrackMouseEvent() is used to start a timer that eventually posts the WM_MOUSEHOVER message. After receiving WM_MOUSEHOVER the box is highlighted if the mouse is still over the box. The fBoxHighLigted variable is set to indicate the state of the box. If the variable is set but the mouse is no longer over the box the area occupied by the box is invalidated so that it is redrawn eventually.