Mensaje    Asunto: WM_MOUSELEAVE / WM_MOUSEENTER Responder citando

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 clase:

Código:

// MouseLeave.cpp
//--------------------------------------------------------------------------------------------------
// TMouseLeave (Versión Hook estilo C++)
// escafandra 2017
// Clase para manejo de WM_MOUSELEAVE de una ventana


#ifndef MouseLeaveCPP
#define MouseLeaveCPP

#include <Windows.h>

#ifndef STRICT
  typedef int  (__stdcall *PLRESULT)();
#else
  typedef WNDPROC PLRESULT;
#endif

//typedef void (__fastcall *POnMouseLeave)(HWND hWnd, BOOL Enter);
typedef void __fastcall(__closure* POnMouseLeave)(HWND hWnd);
typedef void __fastcall(__closure* POnMouseEnter)(HWND hWnd);


class TMouseLeave
{
  private:
  HWND Handle;
  PLRESULT OldWndProc;
  static LRESULT __stdcall DefWndProc(HWND hWnd, UINT Msg, WPARAM WParam, LPARAM LParam)
  {
    TMouseLeave* pMouseLeave = (TMouseLeave*)GetWindowLongPtr(hWnd, GWL_USERDATA);
    if(pMouseLeave)
      return pMouseLeave->WndProc(hWnd, Msg, WParam, LParam);
    else
      return DefWindowProc(hWnd, Msg, WParam, LParam);
  }

  LRESULT __stdcall WndProc(HWND hWnd, UINT Msg, WPARAM WParam, LPARAM LParam)
  {
    if(Msg == WM_MOUSELEAVE && OnMouseLeave)
      OnMouseLeave(Handle);

    else if(Msg == WM_MOUSEMOVE && OnMouseEnter){
      TRACKMOUSEEVENT TE = {sizeof(TRACKMOUSEEVENT)};
      TE.dwFlags = TME_LEAVE;
      TE.hwndTrack = Handle;
      TE.dwHoverTime = HOVER_DEFAULT;
      TrackMouseEvent(&TE);
      OnMouseEnter(Handle);
    }

    return CallWindowProc(OldWndProc, hWnd, Msg, WParam, LParam);
  }

  public:
  POnMouseLeave OnMouseLeave;
  POnMouseEnter OnMouseEnter;

  void SetHandle(HWND hWnd)
  {
    if(hWnd != INVALID_HANDLE_VALUE && hWnd != Handle){
      if(!hWnd){
        SetWindowLong(GetParent(Handle), GWL_USERDATA, 0);
        SetWindowLong(GetParent(Handle), GWL_WNDPROC, (LONG)OldWndProc);
      }
      if(hWnd){
        SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG)this);
        OldWndProc = (PLRESULT)SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG)TMouseLeave::DefWndProc);
      }
      Handle = hWnd;
    }
  }

  TMouseLeave(HWND hWnd = 0): OnMouseLeave(0), OnMouseEnter(0)
  {
    SetHandle(hWnd);
  }

  ~TMouseLeave() {SetHandle(0);}
};
#endif


Lo que sigue es un ejemplo para usarlo con un TButton:
Código:

#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "MouseLeave.cpp"
//---------------------------------------------------------------------------
class TForm2 : public TForm
{
__published:    // IDE-managed Components
  TButton *Button1;
private:
  TMouseLeave ML;
  void __fastcall OnMouseLeave(HWND hWnd);
  void __fastcall OnMouseEnter(HWND hWnd);
public:        // User declarations
  __fastcall TForm2(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm2 *Form2;
//---------------------------------------------------------------------------
#endif


Código:

#include <vcl.h>
#pragma hdrstop

#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
  : TForm(Owner)
{
  ML.SetHandle(Button1->Handle);
  ML.OnMouseLeave = OnMouseLeave;
  ML.OnMouseEnter = OnMouseEnter;
}
//---------------------------------------------------------------------------

void __fastcall TForm2::OnMouseLeave(HWND hWnd)
{
  TButton *B = static_cast<TButton*>(FindControl(hWnd));
  if(B) B->Caption = "Adios";
}

void __fastcall TForm2::OnMouseEnter(HWND hWnd)
{
  TButton *B = static_cast<TButton*>(FindControl(hWnd));
  if(B) B->Caption = "Hola";
}


Saludos.