Delphi How to get cursor position on a control?
Asked Answered
D

3

7

I want to know the position of the cursor on a TCustomControl. How does one go about finding the coordinates?

Dashtikavir answered 11/7, 2011 at 8:56 Comment(0)
N
7

You can use MouseMove event:

procedure TCustomControl.MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Label1.Caption := IntToStr(x) + ' ' + IntToStr(y);       
end;
Neau answered 11/7, 2011 at 9:3 Comment(5)
I am using this: procedure WMMouseMove(var message: TWMMouseMove); message WM_MOUSEMOVE;Dashtikavir
@Robrok, why handle WM_MOUSEMOVE directly when you can assign an OnMouseMove Delphi-style event handler and be done with it? If you insist on using WM_MOUSEMOVE, the coordinates are in msg.Lparam, according to the documentationMurex
+1, and I took the liberty to remove the ShowMessage and replace it with a Label1.Caption := X Y, because setting the Caption is non-modal, you can move the mouse over the control and see it update. ShowMessage is modal, as soon as the mouse is over the control the message pops and nothing else happens until you click "Ok"Murex
Sorry, i didnt see that OnMouseMove is defined in TCustomControl. :)Dashtikavir
@Robrok, if you're writing the control, then you shouldn't use OnMouseMove. That's for users of the control. Writers of controls should override the MouseMove method, as demonstrated here.Theine
C
17

GetCursorPos can be helpful if you can't handle a mouse event:

function GetCursorPosForControl(AControl: TWinControl): TPoint;
var 
  P: TPoint; 
begin
  Windows.GetCursorPos(P);
  Windows.ScreenToClient(AControl.Handle, P );
  result := P;
end;
Cabe answered 11/7, 2011 at 9:57 Comment(1)
FWIW, GetCursorPos doesn't work correctly on 64 bit XP/Vista when the TPoint instance is located at a memory address >2MB. MS fixed this in Windows 7. Myself, I always use GetCursorInfo to sidestep the bug.Rebak
N
7

You can use MouseMove event:

procedure TCustomControl.MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Label1.Caption := IntToStr(x) + ' ' + IntToStr(y);       
end;
Neau answered 11/7, 2011 at 9:3 Comment(5)
I am using this: procedure WMMouseMove(var message: TWMMouseMove); message WM_MOUSEMOVE;Dashtikavir
@Robrok, why handle WM_MOUSEMOVE directly when you can assign an OnMouseMove Delphi-style event handler and be done with it? If you insist on using WM_MOUSEMOVE, the coordinates are in msg.Lparam, according to the documentationMurex
+1, and I took the liberty to remove the ShowMessage and replace it with a Label1.Caption := X Y, because setting the Caption is non-modal, you can move the mouse over the control and see it update. ShowMessage is modal, as soon as the mouse is over the control the message pops and nothing else happens until you click "Ok"Murex
Sorry, i didnt see that OnMouseMove is defined in TCustomControl. :)Dashtikavir
@Robrok, if you're writing the control, then you shouldn't use OnMouseMove. That's for users of the control. Writers of controls should override the MouseMove method, as demonstrated here.Theine
F
5

If you want the cursor position when they click on the control, then use Mouse.CursorPos to get the mouse position, and Control.ScreenToClient to convert this to the position relative to the Control.

procedure TForm1.Memo1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  pt: TPoint;
begin
  pt := Mouse.CursorPos;
  pt := Memo1.ScreenToClient(pt);
  Memo1.Lines.Add(Format('x=%d, y=%d', [pt.X, pt.y]));
end;

EDIT:

As various people have pointed out, this is pointless on a mouse down event. However as TCustomControl.OnMouseDown is protected, it may not always be readily available on third-party controls - mind you I would probably not use a control with such a flaw.

A better example might be an OnDblClick event, where no co-ordinate information is given:

procedure TForm1.DodgyControl1DblClick(Sender: TObject);
var
  pt: TPoint;
begin
  pt := Mouse.CursorPos;
  pt := DodgyControl1.ScreenToClient(pt);
  Memo1.Lines.Add(Format('x=%d, y=%d', [pt.X, pt.y]));
end;
Faculty answered 11/7, 2011 at 9:12 Comment(7)
You get the X and Y coordinates as parameters to the event handler, relative to the control. Your code to grab the coordinates from Mouse.CursorPos and convert them to "control coordinates" is redundant!Murex
Yes. It is intended as demo code, rather than as a recommendation.Faculty
You say: "If you want the cursor position when they click on the control, then use Mouse.CursorPos to get the mouse position, and Control.ScreenToClient to convert this to the position relative to the Control.": I disagree. Use the received X and Y coordinates if you need to know where the user clicked.Murex
@Cosmin - I meant for a control that for whatever reason didn't promote OnMouseDown from protected to public. The code here could be used from OnClick or OnDblClick event. I just choose a bad event handler to call it from. From memory, the OnMouse* events weren't in early versions (maybe introduced in D5 or 6 - they are definitely in D6)Faculty
FWIW, Mouse.CursorPos is subject to the same bug that I describe in @splash's answer.Rebak
methinks - excessive surplus redundancy, coordinates already ready to useKindling
@Gerry This makes it a poor demo then. A good demo would suggest the use of the X and Y parameters.Rebak

© 2022 - 2024 — McMap. All rights reserved.