我怎样才能得到控件,这是在 Delphi 光标下?

pobjuy32  于 2023-03-12  发布在  其他
关注(0)|答案(4)|浏览(137)

我需要与"How to get cursor position on a control?"问题相反的信息。
给定当前的光标位置,如何找到光标当前所在的窗体(在应用程序中)和控件?我需要它的句柄,以便使用Windows.SetFocus(Handle)
作为参考,我使用的是 Delphi 2009。

lyfkaqu1

lyfkaqu11#

建议的解决方案( Delphi XE 6/Windows 8.1/x64)遇到了一些问题:

  • FindVCLWindow不搜索禁用的控件(Enabled=False)。
  • 如果控件被间接禁用(例如,如果Button.Enabled=True,但Button.Parent.Enabled=False),则TWinControl.ControlAtPos不搜索这些控件。

在我的例子中,这是一个问题,因为我需要在鼠标光标下找到任何可见的控件,所以我必须使用我自己的函数FindControlAtPos的实现:

function FindSubcontrolAtPos(AControl: TControl; AScreenPos, AClientPos: TPoint): TControl;
var
  i: Integer;
  C: TControl;
begin
  Result := nil;
  C := AControl;
  if (C=nil) or not C.Visible or not TRect.Create(C.Left, C.Top, C.Left+C.Width, C.Top+C.Height).Contains(AClientPos) then
    Exit;
  Result := AControl;
  if AControl is TWinControl then
    for i := 0 to TWinControl(AControl).ControlCount-1 do
    begin
      C := FindSubcontrolAtPos(TWinControl(AControl).Controls[i], AScreenPos, AControl.ScreenToClient(AScreenPos));
      if C<>nil then
        Result := C;
    end;
end;

function FindControlAtPos(AScreenPos: TPoint): TControl;
var
  i: Integer;
  f,m: TForm;
  p: TPoint;
  r: TRect;
begin
  Result := nil;
  for i := Screen.FormCount-1 downto 0 do
    begin
      f := Screen.Forms[i];
      if f.Visible and (f.Parent=nil) and (f.FormStyle<>fsMDIChild) and 
        TRect.Create(f.Left, f.Top, f.Left+f.Width, f.Top+f.Height).Contains(AScreenPos) 
      then
        Result := f; 
    end;
  Result := FindSubcontrolAtPos(Result, AScreenPos, AScreenPos);
  if (Result is TForm) and (TForm(Result).ClientHandle<>0) then
  begin
    WinAPI.Windows.GetWindowRect(TForm(Result).ClientHandle, r);
    p := TPoint.Create(AScreenPos.X-r.Left, AScreenPos.Y-r.Top);
    m := nil;
    for i := TForm(Result).MDIChildCount-1 downto 0 do
    begin
      f := TForm(Result).MDIChildren[i];
      if TRect.Create(f.Left, f.Top, f.Left+f.Width, f.Top+f.Height).Contains(p) then
        m := f; 
    end;
    if m<>nil then
      Result := FindSubcontrolAtPos(m, AScreenPos, p);
  end;
end;
k7fdbhmy

k7fdbhmy2#

我认为FindVCLWindow将满足您的需要。一旦您在光标下有了窗口控件,您就可以遍历父链来找到窗口所在的窗体。

bvjveswy

bvjveswy3#

如果要了解窗体内某个x,y坐标处的控件
用途

function TWinControl.ControlAtPos(const Pos: TPoint; AllowDisabled: Boolean;
        AllowWinControls: Boolean = False; AllLevels: Boolean = False): TControl;

鉴于您似乎只对应用程序中的表单感兴趣,您可以只查询所有表单。
获得非空结果后,可以使用如下代码查询控件的Handle

伪码

function HandleOfControlAtCursor: THandle;
const
  AllowDisabled = true;
  AllowWinControls = true;
  AllLevels = true;
var
  CursorPos: TPoint
  FormPos: TPoint;
  TestForm: TForm;
  ControlAtCursor: TControl;
begin
  Result:= THandle(0);
  GetCursorPos(CursorPos);
  for each form in my application do begin
    TestForm:= Form_to_test;
    FormPos:= TestForm.ScreenToClient(CursorPos);
    ControlAtCursor:= TestForm.ControlAtPos(FormPos,  AllowDisabled,
                                            AllowWinControls, AllLevels);
    if Assigned(ControlAtCursor) then break;
  end; {for each}
  //Break re-enters here
  if Assigned(ControlAtCursor) then begin
    while not(ControlAtCursor is TWinControl) do 
      ControlAtCursor:= ControlAtCursor.Parent;
    Result:= ControlAtCursor.Handle;
  end; {if}
end;

这也允许您排除某些形式的考虑,如果你这样的愿望。如果你正在寻找简单,我会去大卫和使用FindVCLWindow
P.S.就我个人而言,我会使用goto而不是break,因为使用后藤可以立即清楚地看到break重新进入的位置,但在本例中,这不是一个大问题,因为在break和re-entry点之间没有语句。

46qrfjad

46qrfjad4#

我曾在一个大型项目中工作过,这个项目有很多框架和很多动态创建的控件。当软件运行时,很难弄清楚哪个控件是哪个控件,以及它是在哪里创建的。所以,我编写了这个tiny piece of code,它告诉你哪个控件在鼠标下。只有当程序在调试模式下编译时,我才显示Digger窗体。所以它对客户不可用,而仅对开发者可用。
代码非常非常简单。它全部恢复到一个名为ShowParentTree的递归函数。我们从Digg开始调用ShowParentTree,它在应用程序空闲时被调用:

procedure TfrmDigger.ApplicationEventsIdle(Sender: TObject; var Done: Boolean);
begin
   Digg;
end;

Digg函数看起来像这样,它是由FindVCLWindow完成的:

procedure TfrmDigger.Digg;
VAR Ctrl : TWinControl;
begin
 Ctrl := FindVCLWindow(Mouse.CursorPos); { It will not “see” disabled controls }

 if Ctrl <> NIL then
 begin
  VAR s:= ctrl.Name+ ‘ (‘+ ctrl.ClassName + ‘)’;
  Memo.Text:= s+ #13#10+ ShowParentTree(ctrl, 1);

 Caption := s;
 if ctrl is TLabeledEdit then
 Caption := Caption + ‘ Text: ‘+TLabeledEdit(ctrl).Text;
end;
end;

一旦我们将控件置于鼠标下,ShowParentTree就会通过递归调用自身来挖掘该控件的父控件,以及父控件的父控件,依此类推:

function ShowParentTree(Control: TControl; Depth: Integer): string; { Recursive }
VAR Ctrl: TControl;
begin
  Ctrl:= Control.Parent;
  if Ctrl = NIL
  then Result:= ”
  else
  begin
     Result:= System.StringOfChar(‘ ‘, Depth);
     Inc(Depth);
     Result:= Result+ ‘ ‘+ Ctrl.Name + ‘ (‘+ Ctrl.ClassName+ ‘)’+ #13#10+ ShowParentTree(Ctrl, Depth); { Recursive }
  end;
end;

一旦深入到表单,我们就离开递归调用。
_
警告:无法找到/调查禁用的控件,但FindDragTarget将解决该问题。

相关问题