How to convert screen coordinates to control coordinates with embedded form and multiple displays FMX Windows

Viewed 852

I need to know the mouse position with relative coordinates to a ListView control in the OnItemClickEx event. In a multidisplay system, it works for the app showing in the primary monitor, but does not work when the app is showing for the secondary or third monitor.

The code to know the mouse position is the following:

...
var point : TPointF;
...
point := Screen.MousePos;
point := self.ScreenToClient(point): //we obtain wrong local coordinates for the secondary and third monitor with or without this line 
point := ListView.AbsoluteToLocal(point);

What is the correct way to know the mouse cursor coordinates relative to a control (in this use case a TListView)?

UPDATE

In this example you can see that it works fine in the first Form, but in the embedded form it provides wrong coordinates. It was tested in 10.2 and 10.3 with the same results.

2 Answers

The following code works perfectly with Delphi 10.4.1:

procedure TForm1.ListView1ItemClickEx(
    const Sender        : TObject;
    ItemIndex           : Integer;
    const LocalClickPos : TPointF;
    const ItemObject    : TListItemDrawable);
var
    PtScreen   : TPointF;
    PtForm     : TPointF;
    PtListView : TPointF;
begin
    PtScreen   := Screen.MousePos;
    PtForm     := ScreenToClient(PtScreen);
    PtListView := ListView1.AbsoluteToLocal(PtForm);

    Memo1.Lines.Add('ListView1ItemClickEx ' +
                    PtListView.X.ToString + ', ' + PtListView.Y.ToString);
    Memo1.GoToTextEnd;
end;

procedure TForm1.ListView1MouseMove(
    Sender : TObject;
    Shift  : TShiftState;
    X, Y   : Single);
begin
    Memo1.Lines.Add('ListViewMouseMove  ' +
                    X.ToString + ', ' + Y.ToString);
    Memo1.GoToTextEnd;
end;

This code will display in a memo the coordinates of the mouse as the mouse cursor moves inside the TListView and show the calculated coordinates from the OnItemClickEx event. Both are the same, on the primary monitor or the secondary monitor.

Maybe you are using an old Delphi version where there was a bug. I don't know. Or the error is elsewhere in your code.

Your issue would be solved if you capture the mouse position from ListView OnMouseMove event handler and save it in variable in your form so that it is readily available from OnItemClickEx event handler:

private
    FMouseX : Single;
    FMouseY : Single;


procedure TForm1.ListView1MouseMove(
    Sender : TObject;
    Shift  : TShiftState;
    X, Y   : Single);
begin
    FMouseX := X;
    FMouseY := Y;
end;

procedure TForm1.ListView1ItemClickEx(
    const Sender        : TObject;
    ItemIndex           : Integer;
    const LocalClickPos : TPointF;
    const ItemObject    : TListItemDrawable);
begin
    Memo1.Lines.Add('ListView1ItemClickEx ' +
                    FMouseX.ToString + ', ' + FMouseY.Y.ToString);
    Memo1.GoToTextEnd;
end;

Finally, after doing some research and tests I've found the trick:

...
var point : TPointF;
...
  point := Screen.MousePos;
  point := Application.MainForm.ScreenToClient(point); // The calculation should be with the MainForm, not the embedded!!!
  point := ListView.AbsoluteToLocal(point);
Related