如何在嵌入式表单和多显示器的FMX Windows中将屏幕坐标转换为控件坐标

3

我需要在OnItemClickEx事件中获取相对于ListView控件的鼠标位置。在多显示器系统中,该代码在显示在主监视器的应用程序中有效,但在显示在第二或第三监视器的应用程序中无效。

获取鼠标位置的代码如下:

...
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);

如何正确获取鼠标光标相对于控件的坐标(在这个例子中是一个 TListView)?

更新

这个示例中,您可以看到它在第一个窗体中正常工作,但在嵌入的窗体中提供了错误的坐标。 它已在10.2和10.3中进行了测试,结果相同。

2个回答

1
以下代码在 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;

这段代码将在一个备忘录中显示鼠标在TListView内移动时的坐标,并显示来自OnItemClickEx事件的计算坐标。无论是在主监视器还是次监视器上,两者都相同。
也许您正在使用旧版Delphi,其中存在错误。我不知道。或者错误在您的代码中的其他地方。
如果您从ListView OnMouseMove事件处理程序中捕获鼠标位置并将其保存在表单变量中,则可以轻松从OnItemClickEx事件处理程序中读取该变量,从而解决您的问题。
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;

在我看来,OnMouseMove 不是很优秀,可能有更好的解决方案。 - Xalo
你为什么认为这不是最优解?我一直使用OnMouseMove方法,从未出现过问题。 - fpiette
我的意思是在这种情况下,因为我怀疑有一个更简单和干净的解决方案。通过我找到的解决方案,在其他事件中没有额外的代码涉及,只有当用户点击时才会执行。 - Xalo

0

终于,在进行一些研究和测试后,我找到了窍门:

...
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);

这可能适用于您的情况,因为listview在主窗体中,但这不是一个通用解决方案!我的答案将适用,因为它将ScreenToClient应用于嵌入listview的窗体。 - fpiette
François,请查看我的更新部分中的示例。您可以看到它适用于Form1中的TListView,但是当它嵌入在Form1中时,它不适用于Form2中的TListView。我同意您使用OnMouseMove的解决方案对于一般解决方案是有效的,但在我看来,简单越好,我最终提供的解决方案是所有嵌入式表单在“主机”表单中使用的正确解决方案。 - Xalo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接