如何在Delphi中获取光标下的Control?

时间:2011-07-16 19:14:08

标签: delphi focus cursor setfocus

我需要问题"How to get cursor position on a control?"提出相反的信息。

鉴于当前光标位置,我如何找到表单(在我的应用程序中)和光标当前的控件?我需要它的句柄,以便我可以使用Windows.SetFocus(Handle)

供参考,我正在使用Delphi 2009。

3 个答案:

答案 0 :(得分:3)

我认为FindVCLWindow会满足您的需求。在光标下有窗口控件后,您可以走父链,找到窗口所在的窗体。

答案 1 :(得分:3)

我在使用建议的解决方案时遇到了一些问题(Delphi XE6 / Windows 8.1 x64):

  • FindVCLWindow没有搜索禁用的控件(Enabled = False)。
  • TWinControl.ControlAtPos如果禁用它们,则不会搜索控件 间接(例如,如果Button.Enabled = True,但Button.Parent.Enabled = False)。

在我的情况下这是一个问题,因为我需要在鼠标光标下找到任何可见的控件,所以我必须使用我自己的函数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;

答案 2 :(得分:2)

如果您想知道某个x,y坐标的表单内的控件

使用

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

鉴于您似乎只对应用程序中的表单感兴趣,您可以只查询所有表单。

获得非零结果后,您可以使用以下代码查询控件的句柄

伪码

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;

如果您愿意,这也允许您排除某些表格。如果您正在寻找简单性,我会选择David并使用FindVCLWindow

P.S。就个人而言,我会使用goto而不是休息,因为通过goto可以立即清楚中断重新进入的位置,但在这种情况下,这不是一个大问题,因为在休息和休息之间没有任何陈述重返入口点。