El título lleva a engaño puesto que el mensaje WM_MOUSEENTER no existe aunque 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 VCL 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 con un apunte propuesto por
[Agustin Ortu] en los constructores y el destructor de la clase:
Código Delphi
[-]
unit MouseLeave;
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(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(WND: HWND);
begin
inherited Create;
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
SetHandle(0);
inherited Destroy;
end;
end.
Lo que sigue es un ejemplo para usarlo con un TButton:
Código Delphi
[-]
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
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';
if ME.InheritsFrom(TButton) then
Windows.Beep(1000, 100);
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.
Alguno puede preguntarse porqué no hacer un componente con esta clase o porqué no usar clases comodín Interpuestas para utilizar el modo Hook a la función de tratamiento de mensajes que porpone la VCL. La respuesta es simple, he preferido este modo porque así permite trabajar con el Handle de cualquier ventana más allá del entorno de la VCL y sirve para aplicaciones escritas a bajo nivel, a nivel API.
Saludos.