El título lleva a engaño puesto que el mensaje WM_MOUSEENTER no existe aunque para casi todos, el concepto si.
Se trata de manejar los eventos OnMouseEnter y OnMouseLeave de una ventana en versiones Delphi antiguas que no implementan esta característica, al igual que en versiones Builder de la misma época. También servirá para usarlo con cualquier ventana sin necesidad de que se trate de un control - componente específico.
El mensaje WM_MOUSELEAVE es recibido por una ventana si preparó previamente su solicitud con una llamada a TrackMouseEvent. Simplemente informa que el cursor del ratón abandonó el área cliente de dicha ventana. Para detectar la presencia del cursor en la ventana (WM_MOUSEENTER) basta con gestionar WM_MOUSEMOVE.
Propongo una clase que habilita el tratamiento del mensaje WM_MOUSELEAVE recibido por cualquier ventana (incluidos componentes derivados de TControl) Para conseguirlo realiza un Hook a la función de tratamiento de mensajes realizando un subclassing que genere dos eventos: OnMouseLeave y OnMouseEnter.
Este sería el código de la Unit:
unit MouseLeave; //-------------------------------------------------------------------------------------------------- // TMouseLeave (Versión Hook estilo C++) // escafandra 2017 // Clase para manejo de WM_MOUSELEAVE de una ventana interface uses Windows, Messages; type TOnMouseLeave = procedure(Handle: HWND) of object; TOnMouseEnter = procedure(Handle: HWND) of object; type TMouseLeave = class private Handle: HWND; OldWndProc: Pointer; function WndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall; public OnMouseLeave: TOnMouseLeave; OnMouseEnter: TOnMouseEnter; constructor Create; overload; constructor Create(WND: HWND); overload; destructor Destroy; override; procedure SetHandle(WND: HWND); end; implementation function DefWndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall; var pMouseLeave: TMouseLeave; begin pMouseLeave:= TMouseLeave(GetWindowLong(Handle, GWL_USERDATA)); if pMouseLeave <> nil then Result:= pMouseLeave.WndProc(Handle, Msg, WParam, LParam) else Result:= DefWindowProc(Handle, Msg, WParam, LParam); end; constructor TMouseLeave.Create; begin OnMouseLeave:= nil; OnMouseEnter:= nil; SetHandle(0); end; constructor TMouseLeave.Create(WND: HWND); begin OnMouseLeave:= nil; OnMouseEnter:= nil; SetHandle(WND); end; function TMouseLeave.WndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall; var TE: TTRACKMOUSEEVENT; begin if (Msg = WM_MOUSELEAVE) and (@OnMouseLeave <> nil) then OnMouseLeave(Handle) else if (Msg = WM_MOUSEMOVE) and (@OnMouseEnter <> nil) then begin TE.cbSize:= sizeof(TTRACKMOUSEEVENT); TE.dwFlags:= TME_LEAVE; TE.hwndTrack:= Handle; TE.dwHoverTime:= HOVER_DEFAULT; TrackMouseEvent(TE); OnMouseEnter(Handle); end; Result:= CallWindowProc(OldWndProc, Handle, Msg, WParam, LParam); end; procedure TMouseLeave.SetHandle(WND: HWND); begin if (WND <> INVALID_HANDLE_VALUE) and (WND <> Handle) then begin if WND = 0 then begin SetWindowLong(Handle, GWL_USERDATA, 0); SetWindowLong(Handle, GWL_WNDPROC, LongInt(OldWndProc)); end; if WND <> 0 then begin SetWindowLong(WND, GWL_USERDATA, LongInt(self)); OldWndProc:= Pointer(SetWindowLong(WND, GWL_WNDPROC, LongInt(@DefWndProc))); end; Handle:= WND; end; end; destructor TMouseLeave.Destroy; begin OnMouseLeave:= nil; OnMouseEnter:= nil; SetHandle(0); end; end.
Un ejemplo de uso con un botón, coloco la Unit completa para mostrar todos los pasos de uso y declaración de los eventos:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, MouseLeave; type TForm1 = class(TForm) Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private ME: TMouseLeave; procedure OnMouseLeave(Wnd: HWND); procedure OnMouseEnter(Wnd: HWND); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin ME:= TMouseLeave.Create(Button1.Handle); ME.OnMouseEnter:= OnMouseEnter; ME.OnMouseLeave:= OnMouseLeave; end; procedure TForm1.OnMouseLeave(Wnd: HWND); begin with FindControl(Wnd) as TButton do Caption:= 'Adios'; end; procedure TForm1.OnMouseEnter(Wnd: HWND); begin with FindControl(Wnd) as TButton do Caption:= 'Hola'; end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin ME.Free; end; end.
Se precisa crear tantos objetos TMouseLeave como ventanas a controlar.
Subo el código.
Saludos.