Delphi 正确定位与列表视图项关联的气球提示

Delphi properly position balloon hint associated with a listview item

如何确保我想要与列表视图项目相关联的气球提示位置正确,使其位于相关项目旁边,并始终在屏幕上显示完整的气球文本?

例如,如果我在 Windows 资源管理器中编辑文件名时输入了无效字符,则会弹出一个气球提示无效字符是什么。整个气球始终在屏幕上,即使列表项靠近屏幕边缘或部分离开屏幕。尾部始终位于列表项的中间底部。气泡通常位于尾部的右下方,但如果列表项靠近屏幕底部 and/or 右边缘,则气泡可能位于尾部上方或左侧。

首先,我无法让气泡和尾巴靠近列表项。

procedure TForm1.ListEdited(Sender: TObject; Item: TListItem;
var S: string);
var
  AHint: string;
  R: TRect;
  B : TBalloonHint;
begin
  if TRegEx.IsMatch(S, '[\/:*?"<>|]') then
  begin
    AHint := 'A file name cannot contain any of the following' + sLineBreak +
      'characters:  \/:*?"<>|';
    R := Item.DisplayRect(drBounds);
    R.TopLeft := ClientToScreen(R.TopLeft);
    R.BottomRight := ClientToScreen(R.BottomRight);

    B := TBalloonHint.Create(Self);
    B.Description := AHint;
    B.HideAfter := 5000;
    B.ShowHint(R);

    S := TRegEx.Replace(S, '[\/:*?"<>|]', '');
  end;
end;

我已经尝试了 ShowHint 的各种重载,以及 JEDI 气球提示组件。我还调整了矩形的顶部 属性,当项目位于屏幕的某个区域时,这可能会更好地定位气球,但当项目位于屏幕的其他部分时,气球就会偏离位置屏幕。

Delphi 10.3 里约,Win 7 x64。

DisplayRect 给出相对于包含项目的列表视图的客户端坐标,而不是表单。因此,在转换为屏幕坐标时,您必须使用列表视图作为基础,而不是以下形式:

R := Item.DisplayRect(drBounds);
R.TopLeft := ListView1.ClientToScreen(R.TopLeft);         // <--
R.BottomRight := ListView1.ClientToScreen(R.BottomRight); // <--