Мне нужно знать положение мыши с относительными координатами для элемента управления ListView в событии OnItemClickEx
. В системе с несколькими дисплеями это работает для приложения, отображаемого на основном мониторе, но не работает, когда приложение отображается на дополнительном или третьем мониторе.
Код для определения положения мыши следующий:
...
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 с теми же результатами.
Следующий код отлично работает с 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, и он никогда не подводил.
Я имею в виду этот вариант использования, потому что я подозревал, что есть более простое и чистое решение. В решении, которое я нашел, нет дополнительного кода, связанного с другими событиями, только когда пользователь щелкает.
Наконец, проведя некоторые исследования и тесты, я нашел трюк:
...
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);
Это может сработать для вас, потому что список находится в основной форме, но это не общее решение! Мой ответ будет работать, потому что он применяет ScreenToClient к форме, в которую встроено представление списка.
Франсуа, пожалуйста, посмотрите мой пример в разделе обновлений. Вы можете видеть, что это работает для TListView в Form1, но не работает для TListView в Form2, когда он встроен в form1. Я согласен с вами в том, что ваше решение с OnMouseMove подходит для общего решения, но ИМХО, чем проще, тем лучше, и решение, которое я, наконец, предоставляю, является правильным для варианта использования со всеми встроенными формами в форме «хоста».
ИМХО, OnMouseMove не очень оптимален, и может быть лучшее решение.