ShortCut什么时候开火?

NGL*_*GLN 13 delphi vcl keyboard-shortcuts

昨天我发现了一个键盘ShortCut在我期待它时没有发射的情况.

具体情况是:我在MDI子项上按下了ShortCut键组合以获取ActionList的Action,同时聚焦了MDI表单上的侧栏.

我一直认为ShortCuts可以在全球范围内运作.在哪种情况下,他们不会开火?

NGL*_*GLN 28

这是一个看似简单的问题,答案非常长.首先,我将处理一些基础知识,然后通过VCL代码遵循ShortCut,最终得出 - 我希望 - 一个令人满意的结论.

什么是ShortCut?

ShortCut表示导致操作的一个或多个键的特殊键盘组合.对于为特定键组合赋予意义的程序员来说,特殊意味着特殊.

在Delphi中,ShortCut的类型TShortCut声明为Word范围内的整数(0..65535).ShortCut通常由几个键构成,例如:

CTRL+ K= scCtrl+ Ord('K')= 16384+ 75= 16459.

如何指定ShortCut?

可以将ShortCuts分配给Action的属性ShortCutSecondaryShortCuts属性,或者分配给ShortCutMenuItem 的属性,从而在按下ShortCut的键盘组合时调用Action的OnExecute事件或MenuItem的OnClick事件.

要处理Action的ShortCut,需要启用Action,并将其添加到未挂起的ActionList或附加到已启用的MenuItem.同样,要处理MenuItem的ShortCut,需要将MenuItem添加到菜单中.

快捷方式也可以从所解释的应用程序的,一个形式的或从ApplicationEvents ' OnShortCut事件.在这些事件中,Msg参数将键码保存在其CharCode成员中,并且可能包含特殊键Shift,Ctrl或者Alt可以使用GetKeyState以下方法提取:

procedure TForm1.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
  if (Msg.CharCode = Ord('K')) and (GetKeyState(VK_CONTROL) < 0) then
  begin
    Caption := 'CTRL+K pressed';
    Handled := True;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

如果Handled参数设置为True,则将跳过对该键的任何后续处理.

ShortCut是如何捕获的?

VCL不会保留所有指定快捷方式的列表.(怎么可能?)因此,所有击键都可能是ShortCut.这正是VCL如何解释ShortCuts:通过评估每个按下的键.

Peter Below 通过VCL 写了一篇关于A Key的奥德赛的精彩而全面的文章.简而言之,ShortCut如下:

  • TApplication.Run 拿起发送给应用程序的每条Windows消息,
  • TApplication.ProcessMessageIsKeyMsg将消息传递WM_KEYDOWN焦点控件CN_KEYDOWN消息处理程序的调用- 如果是消息.

ShortCut是如何处理的?

TWinControl.CNKeyDown检查该键是否为菜单键(我们将看到此菜单的定义超出了物理菜单):

  • TWinControl.IsMenuKey首先检查密钥是否是Control的一个ShortCut或其父亲的PopupMenu之一,
    • TMenu.IsShortCut 1)遍历其所有(子)菜单项并OnClick使用ShortCut 调用启用的MenuItem 的事件处理程序(如果有)
  • 如果没有处理,它会通过调用2)来检查密钥是否是Control所在的Form的ShortCut , TCustomForm.IsShortCut
    • OnShortCut表单的情况下被调用时,如果指定,
    • 如果没有处理,它会检查密钥是否是Form的MainMenu(见1)中的ShortCut ,如果有的话,
    • 如果未处理,它会将密钥分派给Form所拥有的所有ActionLists(仍在讨论活动表单).在Delphi版本10(BDS2006)之前,这些ActionLists需要由Form直接拥有并保存在受保护的(在需要时可以进行干预)字段中FActionLists.这被认为是一个错误,并且从BDS2006开始,该字段被淘汰,ActionLists也可以由Form间接拥有.
      • TCustomActionList.IsShortCut遍历其所有操作并调用HandleShortCut已启用的Action,并在其ShortCutSecondaryShortCuts属性中设置ShortCut ,如果有的话,
  • 如果没有处理,Application.IsShortCut则调用(via CM_APPKEYDOWN),
    • 触发OnShortCutApplication事件,这包括OnShortCut项目中所有ApplicationEvents组件的事件(如果已分配),
    • 如果未处理,则调用MainFormIsShortCut例程(参见2)),但仅在启用MainForm时.例如,当活动表格是模态表格时,将禁用主表格.这将触发MainForm事件或将遍历MainForm的所有直接或间接拥有的ActionLists(取决于上面提到的Delphi版本).OnShortCut

那么,ShortCut何时处理?

几时:

  • 为PopupMenu中启用的MenuItem设置,该菜单附加到当前焦点控件或其任何父节点,
  • 为MainMenu中启用的MenuItem设置,该菜单显示在当前活动的Form或MainForm上,但仅在启用MainForm时,
  • 在未挂起的ActionList中设置已启用的Action,该ActionList由当前活动的Form或MainForm拥有,但仅在启用MainForm时,
  • OnShortCut当前活动的Form或MainForm 的情况下捕获,但仅在启用MainForm时,
  • OnShortCutApplicaton或任何ApplicationEvents组件的情况下捕获.

什么时候ShortCut没有处理?

当它设置为:

  • 禁用的MenuItem,
  • 没有菜单的MenuItem,
  • MainMenu中未附加到表单的MenuItem,
  • PopupMenu中附加到兄弟节点的MenuItem,
  • 禁用动作,
  • 没有ActionList的Action,
  • ActionList中的Action,不属于当前活动的Form或MainForm.例如:另一个表单,一个DataModule,应用程序,一个实用程序单元等......
  • ActionList中的一个Action,它不是由BDS2006下面的Delphi版本中当前活动的Form或MainForm直接拥有的.

  • 不错的问答!非常值得一个upvote :) (2认同)