检测用户是通过选项卡还是通过单击获得组件的焦点

Detect if user get focus of a component by tab or by click

如何检测用户是通过 tab 键还是通过鼠标 click 进入组件?

更新 1

实际上是关于 TVirtualStringTree 的,顺便说一句,它正在获得焦点,它打开一个专注于一个或另一列的自定义编辑器。

更新 2

查看下面的代码。

procedure TForm1.Tree1Click(Sender: TObject);
var
  Tree: TVirtualStringTree;
  Click: THitInfo;
  HitNode: PVirtualNode;
  HitColumn: TColumnIndex;
  col: Integer;
begin
  Tree:= Sender as TVirtualStringTree;
  Tree.GetHitTestInfoAt(Mouse.CursorPos.X-Tree.ClientOrigin.X, Mouse.CursorPos.Y-Tree.ClientOrigin.Y, True, Click);

  HitNode:= Click.HitNode;
  if not Assigned(Click.HitNode) and Assigned(Tree.FocusedNode) then
    HitNode:= Tree.FocusedNode;

  HitColumn:= Click.HitColumn;

  //get first visible and editable column
  if (HitColumn <= NoColumn) or
     ((HitColumn > NoColumn) and
      (not (coVisible in Tree.Header.Columns.Items[HitColumn].Options) or
       not (coEditable in Tree.Header.Columns.Items[HitColumn].Options))) then
    if Tree.Header.Columns.Count > 0 then
      for col := 0 to Tree.Header.Columns.Count - 1 do
        if (coVisible in Tree.Header.Columns.Items[col].Options) and
           (coEditable in Tree.Header.Columns.Items[col].Options) then
          begin
            HitColumn:= col;
            Break;
          end;

  if Assigned(HitNode) and (HitColumn > NoColumn) then
    {if (Tree.IsEditing and (HitNode <> Tree.FocusedNode)) or
       ((not Tree.IsEditing) and (HitNode = Tree.FocusedNode)) then}
      Tree.EditNode(HitNode,HitColumn);
end;

procedure TForm1.Tree1Enter(Sender: TObject);
var
  Tree: TVirtualStringTree;
  Click: THitInfo;
  HitNode: PVirtualNode;
  HitColumn: TColumnIndex;
  col: Integer;
begin
  Tree:= Sender as TVirtualStringTree;

  HitNode:= Tree.FocusedNode;

  if not Assigned(Tree.FocusedNode) then
    HitNode:= Tree.GetFirstVisible;

  HitColumn:= NoColumn;

  //get first visible and editable column
  if Tree.Header.Columns.Count > 0 then
    for col := 0 to Tree.Header.Columns.Count - 1 do
      if (coVisible in Tree.Header.Columns.Items[col].Options) and
         (coEditable in Tree.Header.Columns.Items[col].Options) then
        begin
          HitColumn:= col;
          Break;
        end;

  if Assigned(HitNode) and (HitColumn > NoColumn) then
    Tree.EditNode(HitNode,HitColumn);
end;

我想做的是:

如果我点击组件,OnEnter 首先被触发,然后 OnClick,所以问题是编辑节点被触发了 2 次。

为了知道焦点是通过使用 tab 还是通过鼠标单击获得的,您需要做一些侦探工作。

  1. 这是在按 Tab 键时确保焦点的代码。
procedure TBaseVirtualTree.WMKeyUp(var Message: TWMKeyUp);

begin
  inherited;

  case Message.CharCode of
    VK_SPACE:
     .... [snip] ....
     VK_TAB:
       //This method causes a flurry of event handlers to be called.
       EnsureNodeFocused(); 
  end;
end;
  1. VTV 除了标准 OnEnterOnExit 事件外,还有两个焦点更改事件:OnFocusChangedOnFocusChanging
    我不知道哪一个最适合您的需求,您必须进行试验。

我们假设焦点始终是使用鼠标获得的,除非我们可以证明它是使用 tab 获得的。这是一个有点不稳定的假设,但没关系。

首先我们使用插入器覆盖 WMKeyUp 消息处理程序。
WMKeyUp 是私有的,but luckily message handlers can always be overriden, even if they are private

内插器
您可以使用 a trick called an interposer 重新定义 VST 以满足我们的需要。
由于范围规则,'improved' TVST 取代了默认 VST。这不会干扰流媒体或表单创建或任何事情。它只是工作。

type
  TVirtualStringTree = class(VirtualTrees.TVirtualStringTree)
  private
    FTabPressed: boolean;
  protected
    procedure WMKeyUp(var Message: TWMKeyUp); message WM_KEYUP;
    property TabPressed: boolean read FTabPressed;
  end;

  TForm56 = class(TForm)
    VirtualStringTree1: TVirtualStringTree;
    ....


procedure TVirtualStringTree.WMKeyUp(var Message: TWMKeyUp);
begin
  case Message.CharCode of
    VK_TAB: FTabPressed:= true;
    else FTabPressed:= false;
  end; {case}
  inherited;
  FTabPressed:= false;
end;

焦点事件处理程序
使用这 两个 三个事件处理程序之一。

//use the standard onEnter....
procedure TForm56.VirtualStringTree1Enter(Sender: TObject);
begin
    if VirtualStringTree1.TabPressed then .....
  else ....
end;

//... or use this event, or...
procedure TForm56.VirtualStringTree1FocusChanged(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex);
begin
  if VirtualStringTree1.TabPressed then .....
  else ....
end;

//.... this event if you want to mess with the focus.
procedure TForm56.VirtualStringTree1FocusChanging(Sender: TBaseVirtualTree;
  OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex;
  var Allowed: Boolean);
begin
  Allowed:= VirtualStringTree1.TabPressed; //just a silly example.
end;

因为通过单击获得焦点总是会在 onEnter 事件之前生成 mousedown 事件,您可以在 mousedown 中设置一个事件说 'gMousedown' := true 然后在 OnEnter 事件中您可以检查 mousedown。不要忘记在 onMouseUp 事件中将 gMousedown 重置为 false。