我如何知道控件何时可以聚焦?

问题描述 投票:0回答:2

我有自己的 Treeview 控件,源自

TCustomTreeView

我在类中添加了一些我自己的过程,例如添加节点。当在运行时调用此过程时,我希望选择新添加的节点并聚焦树视图,以便突出显示新节点。

以下是摘录:

procedure TMyTreeView.AddGroup(AName: string);
var
  Node: TTreeNode;
  Obj: TGroup;

  procedure AddToTree;
  begin
    Obj := TGroup.Create(AName);
    FGroups.Add(Obj);

    Node := Items.AddObject(Node, AName, Obj);
    with Node do
    begin
      ImageIndex := 0;
      SelectedIndex := 0;
    end;

    Selected := Node;
    SetFocus;
  end;

begin
  Node := nil;
  AddToTree;
end;

上述方法有效,但从表单调用时遇到常见错误消息

OnCreate
事件:

无法聚焦禁用或不可见的窗口

我知道您可以使用

OnActivate
事件,或者根本不使用
OnCreate
这不会导致错误,但任何可能使用该组件的其他人可能没有意识到这一点。

所以我想知道是否有办法确定我的 Treeview(或任何控件)是否能够接收焦点,然后我可以添加一些自己的检查,例如:

if ControlIsFocusable then
begin
  Selected := Node;
  SetFocus;
end;

我知道有一个

Loaded
程序可以覆盖,它告诉我们控件何时加载,但这只在第一次运行时有效。如果控件在运行时被用户隐藏(或者开始时不可见),
Cannot focus a disabled or invisible window
错误仍然会出现。

不在调试器中运行时执行此操作的肮脏方法是:

try
  Selected := Node;
  SetFocus;
except
end;

但这违背了目的,我讨厌以这种方式处理错误。

所以基本上我想知道是否有一种方法可以确定控件是否可以接收焦点,以便我们可以将焦点设置到它?

delphi delphi-xe
2个回答
4
投票

我不会回答你提出的问题,因为我认为你做错了。

控件不应自行调用

SetFocus
。我无法想象这种行为是正确的。形式、应用程序或框架应确定焦点。不是控制。

想象一下当您的表单包含两个这样的控件时会发生什么?想象一下使用键盘聚焦一个按钮,然后用 SPACE 栏按下该按钮。如果附加到按钮的操作调用控件的方法,然后更改焦点,那么您就违反了平台 UI 准则。您的控件现在给任何尝试使用它的应用程序带来了严重的负担。


0
投票

我下面的回答并没有直接回答您的问题,但它仍然相关,因为您依赖 CanFocus。 CanFocus 撒了谎。你不应该依赖它。文档也是错误的。更准确地说,即使控件不可聚焦,CanFocus 也可以返回 True。在这种情况下,将会引发异常。

所以,用这个代替:

function CanFocus(Control: TWinControl): Boolean;   
begin
 Result:= Control.CanFocus AND Control.Enabled AND Control.Visible;
 if Result
 AND NOT Control.InheritsFrom(TForm)
 then
   { Recursive call:
     This control might be hosted by a panel, which could be also invisible/disabled.
     So, we need to check all the parents down the road, until we encounter the parent Form.
     Also see: GetParentForm }
   Result:= CanFocus(Control.Parent); { Parent of a control could be nil, but in this case Control.CanFocus will deal with that.}
end;


procedure SetFocus(Control: TWinControl);
begin
 if CanFocus(Control)
 then Control.SetFocus;
end;

PS:在Lazarus下CanFocus工作正常。

2023 年更新

请参阅这篇有关 CanFocus 的新文章以及如何修复它。 您甚至可以找到一个工具,可以用固定函数 SetFocus() 替换所有 Control.SetFocus 方法。


理由:

J 提供了一个很好的答案,但我不喜欢类助手,因为如果同一类有多个类助手,则将使用唯一的一个。这个过程几乎是“掷骰子”:“uses”子句中的单元顺序决定了将应用哪个助手。我不喜欢编程语言中如此多的随机性。

© www.soinside.com 2019 - 2024. All rights reserved.