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

发布于 2024-11-24 14:45:02 字数 261 浏览 2 评论 0原文

我需要与问题 "如何获取光标位置相反的信息在控件上?” 问道。

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

作为参考,我使用的是 Delphi 2009。

I need the opposite information that the question "How to get cursor position on a control?" asks.

Given the current cursor position, how can I find the form (in my application) and the control that the cursor is currently over? I need the handle to it so that I can use Windows.SetFocus(Handle).

For reference, I'm using Delphi 2009.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

十二 2024-12-01 14:45:02

我在使用建议的解决方案时遇到了一些问题(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;

I experienced some problems with suggested solutions (Delphi XE6/Windows 8.1/x64):

  • FindVCLWindow doesn't search disabled controls (Enabled=False).
  • TWinControl.ControlAtPos doesn't search controls if they are disabled
    indirectly (for example if Button.Enabled=True, but Button.Parent.Enabled=False).

In my case it was a problem, because i need to find any visible control under the mouse cursor, so i have to use my own implementation of function 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;
白首有我共你 2024-12-01 14:45:02

我认为 FindVCLWindow 会满足您的需求。一旦将窗口控件置于光标之下,您就可以沿着父链查找窗口所在的窗体。

I think FindVCLWindow will meet your needs. Once you have the windowed control under the cursor you can walk the parent chain to find the form on which the window lives.

如梦 2024-12-01 14:45:02

如果您想知道表单内位于某个 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。

PS 就我个人而言,我会使用 goto 而不是中断,因为使用 goto 可以立即清楚中断重新进入的位置,但在这种情况下,这不是一个大问题,因为之间没有语句中断点和重新进入点。

If you want to know the control inside a form that is at a certain x,y coordinate

Use

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

Given the fact that you seem only interested in forms inside your application, you can just query all forms.

Once you get a non-nil result, you can query the control for its Handle, with code like the following

Pseudo code

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;

This also allows you to exclude certain forms from consideration should you so desire. If you're looking for simplicity I'd go with David and use FindVCLWindow.

P.S. Personally I'd use a goto rather than a break, because with a goto it's instantly clear where the break re-enters, but in this case it's not a big issue because there are no statements in between the break and the re-entry point.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文