【问题标题】:How to use FindVCLWindow on a TGraphicControl that is underneath a TPaintBox?如何在 TPaintBox 下方的 TGraphicControl 上使用 FindVCLWindow?
【发布时间】:2014-12-23 19:07:15
【问题描述】:

我正在尝试在 TGraphicControl 组件(例如 TLabel 和 TImage)上使用 FindVCLWindow,以便我可以在标签或状态栏中返回它们的名称,但我遇到了一些问题。

问题 1

第一个问题是FindVCLWindow 仅适用于TWinControl 而不适用于TGraphicControl 的后代,所以我尝试使用以下似乎可行的方法:

function FindVCLGraphicWindow(const Pos: TPoint): TGraphicControl;
var
  Window: TWinControl;
  Ctrl: TControl;
begin
  Result := nil;
  Window := FindVCLWindow(Pos);

  if Window <> nil then
  begin
    Ctrl := Window.ControlAtPos(Window.ScreenToClient(Pos), True, True, True);
    if Ctrl is TGraphicControl then
    begin
      Result := TGraphicControl(Ctrl);
    end;
  end;
end;

我想这是一个问题,因为它似乎有效,但也许有更好的解决方案?

问题 2

我遇到的最大问题是,我需要使用上述函数处理的标签和图像位于 TPaintBox 下方,因此标签或图像组件似乎无法接收或响应鼠标移动。换句话说,除非标签或图像位于顶部(即BringToFront),否则该功能不起作用。

我记得不久前从我在这里发布的另一个问题中了解到,通过将 TPaintbox 设置为 Enabled := False 将允许底层控件接收鼠标消息等。

但是,使用上述函数总是返回 nil/false,因为它“看不到”疼痛盒下方的图形控件。

所以我的主要问题是,如何在 TPaintBox 后面的 TGraphicControl 上使用像 FindVCLWindow 这样的函数?

例如,如果以下控件位于面板内:

Image1.SendToBack;
Image2.SendToBack;
Label1.SendToBack;
Label2.SendToBack;
PaintBox1.BringToFront;

上面的方法只有在它们不在画框后面时才有效。

不能将图像和标签放在颜料盒上方,它们必须位于颜料盒后面,但这样做不会响应上述功能。

那么我该如何让它工作呢?该函数似乎只能看到画框,而不是底层图像和标签?

【问题讨论】:

  • 如果你把一堆win控件放在一起,这对FindVCLWindow也是有效的,不是吗?迭代所有控件并选择您喜欢的控件。
  • @SertacAkyuz 我想我知道你在说什么,但我需要FindVCLGraphicWindow 忽略颜料盒,而是在它下面查看。是的,我知道您可以使用 for 循环来迭代父项内的所有控件,但我需要直接位于鼠标光标下方的控件,并且 FindVCLWindow 我认为这将非常适合我的需求,只是它无法获取图像和标签下的标签油漆盒。我将继续尝试一个新项目...

标签: delphi


【解决方案1】:

TWinControl.ControlAtPos的第二个参数指定是否允许禁用控件。您已将其设置为True,因此它将返回禁用的 PaintBox。将其设置为False,您的函数将返回 PaintBox 背面的标签和图像:

function FindVCLGraphicWindow(const Pos: TPoint): TGraphicControl;
var
  Window: TWinControl;
  Ctrl: TControl;
begin
  Result := nil;
  Window := FindVCLWindow(Pos);
  if Window <> nil then
  begin
    Ctrl := Window.ControlAtPos(Window.ScreenToClient(Pos), False, True, True);
    if Ctrl is TGraphicControl then
    begin
      Result := TGraphicControl(Ctrl);
    end;
  end;
end;

【讨论】:

  • 感谢NGLN!事实上,如果我记得它可能是你之前使用 Enabled 画框提示的帮助/asnwer :)
  • 谢谢,你又挽救了一天;)
  • > "将返回标签和图像" > 它只会返回一个控件。如果你想要全部/部分,你需要迭代。关于接受,我认为这个问题具有误导性。
  • @SertacAkyuz 我不确定这个问题是否具有误导性,或者只是没有尽我所能解释。我只是在运行时将一些图像和标签作为父级添加到面板中,然后在这些图像和标签之上放置一个油漆盒。使用 FindVCLWindow 之类的东西,我需要获取位于颜料盒正下方的控件(图像或标签)。我的原始代码和 NGLN 找到的更正正是我所需要的。现在我只使用GraphicControl := FindVCLGraphicWindow 然后我可以检查它是标签还是图像等,if GraphicControl.ClassType = TImage then 等。
  • @Blobby - ".. 我需要获取位于颜料盒正下方的控件(图像或标签).." - 这不是问题要问的。请阅读您提供的涉及多个控件的示例。因此 Deltic 的回答...
【解决方案2】:

您似乎希望在某个位置找到所有控件,然后根据应用程序中的上下文忽略其中一个/一些控件。似乎您正试图将颜料盒下方的控件用作某种可点击的“热点”。

您的问题是您正在使用一种方法,该方法涉及从给定位置定位 单个 控件的函数,并且该函数必须实现它自己的规则来确定哪个 一个 可能 许多 这样的控件,它实际上会返回。该函数中的规则不适用于您的需要。

那么显而易见的答案是,您需要一种允许您使用 your 规则的方法,而不是其他函数中的规则。即不要使用该功能。 :)

相反,您应该简单地迭代所有可能满足您的条件的控件。也就是说,form上的控件在您需要的位置。

要获得表单,您可以按原样使用 VCL 函数来识别某个点的 VCL 控件,并从中确定放置该控件的表单:

form := GetParentForm(FindVCLWindow(ptPos));

一旦您参与了表单,您就可以简单地遍历控件以查找特定兴趣点的控件。在 VCL 中,Controls 属性标识某个父控件的所有子控件,因此您不能使用它来查找窗体上其他控件的子控件(没有一些递归)。

Components 属性标识 ALL 由其他组件拥有的组件。在 VCL 中,表单拥有在设计时放置在其上的所有组件(以及在运行时放置的任何其他组件,只要将表单指定为其所有者),因此您可以使用此 Components 属性遍历表单上的所有组件,无论它们是可视控件、非可视控件、窗口化、图形等:

var
  i: Integer;
  comp: TComponent;
  ctrl: TControl absolute comp;
begin
  result     := NIL;
  bIsHotspot := FALSE;

  form := GetParentForm(FindVCLWindow(ptPos));

  if NOT Assigned(form) then                       // No form = no control to find
    EXIT;

  ptPos := form.ScreenToClient(ptPos);             // pt must be converted to form client co-ords  

  for i := 0 to Pred(form.ComponentCount) do
  begin
    comp := form.Components[i];

    if NOT (comp is TControl) then                 // Only interested in visual controls
      CONTINUE;

    if NOT PtInRect(ctrl.BoundsRect, ptPos) then   // Only controls at the required position 
      CONTINUE;

    // Is this a paintbox (= potential hotspot) or some other control ?

    if (ctrl is TPaintBox) then                      
      bIsHotspot := TRUE
    else
      result := ctrl;

    // If we have now identified a hotspot AND some other control then we're done

    if bIsHotspot and Assigned(result) then
      BREAK;
  end;

  // If we didn't find a hotspot then any other control we may have found is NOT the result

  if NOT bIsHotspot then
    result := NIL;
end;

此例程遍历表单上的所有组件,跳过任何不是可视控件或不在所需位置的组件。

对于视觉控件,它会测试 TPaintbox 以确定指定位置 ptPos 是否代表潜在热点。如果控件不是热点,那么它是一个潜在的结果,假设在同一位置也找到了(或已经找到了)油漆盒。

如果它在指定位置找到一个画框一些其他控件,则结果是非画框控件。如果它在遍历所有组件之前找到两者,则例程停止迭代,以提高效率(这意味着热点控件不能重叠,因为此例程仅找到与其他控件匹配的“第一个”)。

否则结果为 NIL。

上述例程并非 100% 完成,最后 20% 左右留作练习,以最合适的方式合并到您的代码中。您当然可以对其进行调整,以实现识别控件或组件所需的任何规则。

【讨论】:

  • 感谢您的专门回答。 NGLN 现在已经解决了我的问题,但是你的回答给了我一些思考的想法。是的,在某种程度上,我认为底层图像就像一个热点。
猜你喜欢
  • 2015-12-17
  • 2011-05-09
  • 2013-12-03
  • 1970-01-01
  • 2019-01-31
  • 1970-01-01
  • 2010-11-07
  • 1970-01-01
  • 2015-03-14
相关资源
最近更新 更多