Ir al contenido



Foto

[MULTILENGUAJE] WM_MOUSELEAVE / WM_MOUSEENTER


  • Por favor identifícate para responder
9 respuestas en este tema

#1 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.689 mensajes
  • LocationMadrid - España

Escrito 02 febrero 2017 - 02:54

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:


delphi
  1. unit MouseLeave;
  2.  
  3. //--------------------------------------------------------------------------------------------------
  4. // TMouseLeave (Versión Hook estilo C++)
  5. // escafandra 2017
  6. // Clase para manejo de WM_MOUSELEAVE de una ventana
  7.  
  8. interface
  9.  
  10. uses Windows, Messages;
  11.  
  12. type
  13. TOnMouseLeave = procedure(Handle: HWND) of object;
  14. TOnMouseEnter = procedure(Handle: HWND) of object;
  15.  
  16. type
  17. TMouseLeave = class
  18. private
  19. Handle: HWND;
  20. OldWndProc: Pointer;
  21. function WndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall;
  22. public
  23. OnMouseLeave: TOnMouseLeave;
  24. OnMouseEnter: TOnMouseEnter;
  25. constructor Create; overload;
  26. constructor Create(WND: HWND); overload;
  27. destructor Destroy; override;
  28. procedure SetHandle(WND: HWND);
  29. end;
  30.  
  31. implementation
  32.  
  33.  
  34. function DefWndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall;
  35. var
  36. pMouseLeave: TMouseLeave;
  37. begin
  38. pMouseLeave:= TMouseLeave(GetWindowLong(Handle, GWL_USERDATA));
  39. if pMouseLeave <> nil then
  40. Result:= pMouseLeave.WndProc(Handle, Msg, WParam, LParam)
  41. else
  42. Result:= DefWindowProc(Handle, Msg, WParam, LParam);
  43. end;
  44.  
  45. constructor TMouseLeave.Create;
  46. begin
  47. OnMouseLeave:= nil;
  48. OnMouseEnter:= nil;
  49. SetHandle(0);
  50. end;
  51.  
  52. constructor TMouseLeave.Create(WND: HWND);
  53. begin
  54. OnMouseLeave:= nil;
  55. OnMouseEnter:= nil;
  56. SetHandle(WND);
  57. end;
  58.  
  59. function TMouseLeave.WndProc(Handle: HWND; Msg: DWORD; WParam: Longint; LParam: Longint): Longint; stdcall;
  60. var
  61. TE: TTRACKMOUSEEVENT;
  62. begin
  63. if (Msg = WM_MOUSELEAVE) and (@OnMouseLeave <> nil) then
  64. OnMouseLeave(Handle)
  65.  
  66. else if (Msg = WM_MOUSEMOVE) and (@OnMouseEnter <> nil) then
  67. begin
  68. TE.cbSize:= sizeof(TTRACKMOUSEEVENT);
  69. TE.dwFlags:= TME_LEAVE;
  70. TE.hwndTrack:= Handle;
  71. TE.dwHoverTime:= HOVER_DEFAULT;
  72. TrackMouseEvent(TE);
  73. OnMouseEnter(Handle);
  74. end;
  75. Result:= CallWindowProc(OldWndProc, Handle, Msg, WParam, LParam);
  76. end;
  77.  
  78.  
  79. procedure TMouseLeave.SetHandle(WND: HWND);
  80. begin
  81. if (WND <> INVALID_HANDLE_VALUE) and (WND <> Handle) then
  82. begin
  83. if WND = 0 then
  84. begin
  85. SetWindowLong(Handle, GWL_USERDATA, 0);
  86. SetWindowLong(Handle, GWL_WNDPROC, LongInt(OldWndProc));
  87. end;
  88. if WND <> 0 then
  89. begin
  90. SetWindowLong(WND, GWL_USERDATA, LongInt(self));
  91. OldWndProc:= Pointer(SetWindowLong(WND, GWL_WNDPROC, LongInt(@DefWndProc)));
  92. end;
  93. Handle:= WND;
  94. end;
  95. end;
  96.  
  97. destructor TMouseLeave.Destroy;
  98. begin
  99. OnMouseLeave:= nil;
  100. OnMouseEnter:= nil;
  101. SetHandle(0);
  102. end;
  103.  
  104. 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:


delphi
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7. Dialogs, StdCtrls, MouseLeave;
  8.  
  9. type
  10. TForm1 = class(TForm)
  11. Button1: TButton;
  12. procedure FormCreate(Sender: TObject);
  13. procedure FormClose(Sender: TObject; var Action: TCloseAction);
  14. private
  15. ME: TMouseLeave;
  16. procedure OnMouseLeave(Wnd: HWND);
  17. procedure OnMouseEnter(Wnd: HWND);
  18. public
  19. { Public declarations }
  20. end;
  21.  
  22. var
  23. Form1: TForm1;
  24.  
  25. implementation
  26.  
  27. {$R *.dfm}
  28.  
  29. procedure TForm1.FormCreate(Sender: TObject);
  30. begin
  31. ME:= TMouseLeave.Create(Button1.Handle);
  32. ME.OnMouseEnter:= OnMouseEnter;
  33. ME.OnMouseLeave:= OnMouseLeave;
  34. end;
  35.  
  36. procedure TForm1.OnMouseLeave(Wnd: HWND);
  37. begin
  38. with FindControl(Wnd) as TButton do Caption:= 'Adios';
  39. end;
  40.  
  41. procedure TForm1.OnMouseEnter(Wnd: HWND);
  42. begin
  43. with FindControl(Wnd) as TButton do Caption:= 'Hola';
  44. end;
  45.  
  46. procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
  47. begin
  48. ME.Free;
  49. end;
  50.  
  51. end.

Se precisa crear tantos objetos TMouseLeave como ventanas a controlar.
 
Subo el código.
 
 
Saludos.

Archivos adjuntos


  • 6

#2 Dante

Dante

    Advanced Member

  • Miembros
  • PipPipPip
  • 78 mensajes

Escrito 02 febrero 2017 - 08:39

jejej voy a revisar esto seguro q me resuleve algunos problemitas con mi aplicación q se me marea con estas funciones.
  • 0

#3 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.689 mensajes
  • LocationMadrid - España

Escrito 02 febrero 2017 - 10:07

jejej voy a revisar esto seguro q me resuleve algunos problemitas con mi aplicación q se me marea con estas funciones.

 

Si estás usando Berlin busca esos eventos en los controles, es posible que no te haga falta este truco. ;)

 

Saludos.


  • 1

#4 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.689 mensajes
  • LocationMadrid - España

Escrito 02 febrero 2017 - 10:12

Para los amantes de Builder es sus antiguas versiones y para algunos casos de las nuevas, publico el mismo truco en C++


cpp
  1. //--------------------------------------------------------------------------------------------------
  2. // TMouseLeave (Versión Hook estilo C++)
  3. // escafandra 2017
  4. // Clase para manejo de WM_MOUSELEAVE de una ventana
  5.  
  6.  
  7. #ifndef MouseLeaveCPP
  8. #define MouseLeaveCPP
  9.  
  10. #include <Windows.h>
  11.  
  12. #ifndef STRICT
  13. typedef int (__stdcall *PLRESULT)();
  14. #else
  15. typedef WNDPROC PLRESULT;
  16. #endif
  17.  
  18. //typedef void (__fastcall *POnMouseLeave)(HWND hWnd, BOOL Enter);
  19. typedef void __fastcall(__closure* POnMouseLeave)(HWND hWnd);
  20. typedef void __fastcall(__closure* POnMouseEnter)(HWND hWnd);
  21.  
  22.  
  23. class TMouseLeave
  24. {
  25. private:
  26. HWND Handle;
  27. PLRESULT OldWndProc;
  28. static LRESULT __stdcall DefWndProc(HWND hWnd, UINT Msg, WPARAM WParam, LPARAM LParam)
  29. {
  30. TMouseLeave* pMouseLeave = (TMouseLeave*)GetWindowLongPtr(hWnd, GWL_USERDATA);
  31. if(pMouseLeave)
  32. return pMouseLeave->WndProc(hWnd, Msg, WParam, LParam);
  33. else
  34. return DefWindowProc(hWnd, Msg, WParam, LParam);
  35. }
  36.  
  37. LRESULT __stdcall WndProc(HWND hWnd, UINT Msg, WPARAM WParam, LPARAM LParam)
  38. {
  39. if(Msg == WM_MOUSELEAVE && OnMouseLeave)
  40. OnMouseLeave(Handle);
  41.  
  42. else if(Msg == WM_MOUSEMOVE && OnMouseEnter){
  43. TRACKMOUSEEVENT TE = {sizeof(TRACKMOUSEEVENT)};
  44. TE.dwFlags = TME_LEAVE;
  45. TE.hwndTrack = Handle;
  46. TE.dwHoverTime = HOVER_DEFAULT;
  47. TrackMouseEvent(&TE);
  48. OnMouseEnter(Handle);
  49. }
  50.  
  51. return CallWindowProc(OldWndProc, hWnd, Msg, WParam, LParam);
  52. }
  53.  
  54. public:
  55. POnMouseLeave OnMouseLeave;
  56. POnMouseEnter OnMouseEnter;
  57.  
  58. void SetHandle(HWND hWnd)
  59. {
  60. if(hWnd != INVALID_HANDLE_VALUE && hWnd != Handle){
  61. if(!hWnd){
  62. SetWindowLong(GetParent(Handle), GWL_USERDATA, 0);
  63. SetWindowLong(GetParent(Handle), GWL_WNDPROC, (LONG)OldWndProc);
  64. }
  65. if(hWnd){
  66. SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG)this);
  67. OldWndProc = (PLRESULT)SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG)TMouseLeave::DefWndProc);
  68. }
  69. Handle = hWnd;
  70. }
  71. }
  72.  
  73. TMouseLeave(HWND hWnd = 0)
  74. {
  75. OnMouseLeave = 0;
  76. OnMouseEnter = 0;
  77. SetHandle(hWnd);
  78. }
  79.  
  80. ~TMouseLeave() {SetHandle(0);}
  81. };
  82. #endif

Con una pequeña aplicación de ejemplo similar a la versión Delphi:


cpp
  1. #ifndef Unit2H
  2. #define Unit2H
  3. //---------------------------------------------------------------------------
  4. #include <Classes.hpp>
  5. #include <Controls.hpp>
  6. #include <StdCtrls.hpp>
  7. #include <Forms.hpp>
  8. #include "MouseLeave.cpp"
  9. //---------------------------------------------------------------------------
  10. class TForm2 : public TForm
  11. {
  12. __published: // IDE-managed Components
  13. TButton *Button1;
  14. private:
  15. TMouseLeave ML;
  16. void __fastcall OnMouseLeave(HWND hWnd);
  17. void __fastcall OnMouseEnter(HWND hWnd);
  18. public: // User declarations
  19. __fastcall TForm2(TComponent* Owner);
  20. };
  21. //---------------------------------------------------------------------------
  22. extern PACKAGE TForm2 *Form2;
  23. //---------------------------------------------------------------------------
  24. #endif


cpp
  1. //---------------------------------------------------------------------------
  2.  
  3. #include <vcl.h>
  4. #pragma hdrstop
  5.  
  6. #include "Unit2.h"
  7. //---------------------------------------------------------------------------
  8. #pragma package(smart_init)
  9. #pragma resource "*.dfm"
  10. TForm2 *Form2;
  11. //---------------------------------------------------------------------------
  12. __fastcall TForm2::TForm2(TComponent* Owner)
  13. : TForm(Owner)
  14. {
  15. ML.SetHandle(Button1->Handle);
  16. ML.OnMouseLeave = OnMouseLeave;
  17. ML.OnMouseEnter = OnMouseEnter;
  18. }
  19. //---------------------------------------------------------------------------
  20.  
  21. void __fastcall TForm2::OnMouseLeave(HWND hWnd)
  22. {
  23. TButton *B = static_cast<TButton*>(FindControl(hWnd));
  24. if(B) B->Caption = "Adios";
  25. }
  26.  
  27. void __fastcall TForm2::OnMouseEnter(HWND hWnd)
  28. {
  29. TButton *B = static_cast<TButton*>(FindControl(hWnd));
  30. if(B) B->Caption = "Hola";
  31. }

Subo el código.

Archivos adjuntos


  • 2

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 13.615 mensajes
  • LocationMéxico

Escrito 02 febrero 2017 - 11:23

Por demás interesante, gracias amigo.

 

Saludos


  • 0

#6 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 02 febrero 2017 - 04:08

Maestro escafandra, si me pongo en modo "programadoril", no seria el codigo de los constructores y del destructor de la clase inncesarios? Se supone que el constructor heredado de TObject inicializa todas las variables internas a cero. Obviamente que la version sobrecargada que acepta un Handle y lo asigna es un tema aparte
 
Yo lo simplificaria asi:


delphi
  1. type
  2. TMouseLeave = class
  3. public
  4. constructor Create(const WND: HWND);
  5. destructor Destroy; override;
  6. end;
  7.  
  8. constructor TMouseLeave.Create(const WND: HWND);
  9. begin
  10. inherited Create;
  11. SetHandle(WND);
  12. end;
  13.  
  14. destructor TMouseLeave.Destroy;
  15. begin
  16. SetHandle(0);
  17. inherited Destroy;
  18. end;

 
 
Por lo demas, aprobado  :p  :p  :p


Editado por Agustin Ortu, 02 febrero 2017 - 04:09 .

  • 1

#7 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.689 mensajes
  • LocationMadrid - España

Escrito 02 febrero 2017 - 05:15

Maestro escafandra, si me pongo en modo "programadoril", no seria el codigo de los constructores y del destructor de la clase inncesarios? Se supone que el constructor heredado de TObject inicializa todas las variables internas a cero. Obviamente que la version sobrecargada que acepta un Handle y lo asigna es un tema aparte
 
Yo lo simplificaria asi:


delphi
  1. type
  2. TMouseLeave = class
  3. public
  4. constructor Create(const WND: HWND);
  5. destructor Destroy; override;
  6. end;
  7.  
  8. constructor TMouseLeave.Create(const WND: HWND);
  9. begin
  10. inherited Create;
  11. SetHandle(WND);
  12. end;
  13.  
  14. destructor TMouseLeave.Destroy;
  15. begin
  16. SetHandle(0);
  17. inherited Destroy;
  18. end;

 
 
Por lo demas, aprobado  :p  :p  :p

 

Buena observación, vicios que tengo de C++. La clase original en C++ no se deriva de ninguna otra a propósito, no deriva de TObject, es el motivo por el que la versión delphi permanece fiel inicializando las variables, aunque como dices, podría obviarlo. En C++ una clase base no se deriva de nada y el constructor debe especificarlo todo.

 

Esperaba incluso que alguien propusiera clases comodín para delphi y no le faltaría razón, ahorran código al romper la privacidad y protección de métodos y podría usarse el sistema de subclassing de la VCL, pero la hace dependiente de esa librería y quería evitarlo. En C++ no existen las clases comodín, impidiendo romper la protección inherente al diseño de una clase, aunque existen clases amigas. En su momento pensé en usar ese truco de Delphi, pero preferí un diseño más similar a C++ e independiente por completo de la VCL para que sirva para cualquier ventana y en aplicaciones API puras. :)

 

 

Saludos.


  • 1

#8 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 775 mensajes
  • LocationArgentina

Escrito 02 febrero 2017 - 07:56

La clase comodin como seria? Te referis a las clases "interpuestas"?


delphi
  1. type
  2. TForm = class(Forms.TForm)
  3. // metodos propiedades etc
  4. end;
  5.  
  6. TForm1 = class(TForm)
  7. end;


  • 0

#9 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.689 mensajes
  • LocationMadrid - España

Escrito 03 febrero 2017 - 12:27

 

La clase comodin como seria? Te referis a las clases "interpuestas"?


delphi
  1. type
  2. TForm = class(Forms.TForm)
  3. // metodos propiedades etc
  4. end;
  5.  
  6. TForm1 = class(TForm)
  7. end;

 

Exacto.

 

 

Saludos.


  • 0

#10 sir.dev.a.lot

sir.dev.a.lot

    Advanced Member

  • Miembros
  • PipPipPip
  • 533 mensajes
  • Location127.0.0.1

Escrito 04 febrero 2017 - 07:37

Gracias por el tiempo invertido @Escafandra

 

El Codigo sera muy util para mi y algo que tengo... +1 (y)

 

Parece haber un problema con el sistema de Votaciones en el Foro, no puedo dejar puntos... me muestra este mensaje

 

 

 

Hubo un problema almacenando tu voto de reputación

 

 

Saludos!


  • 2