ShortCut什么时候开火?

时间:2014-11-22 22:29:04

标签: delphi keyboard-shortcuts vcl

昨天我发现了一种键盘ShortCut在我预期时没有发射的情况。

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

我总是认为ShortCuts可以在全球范围内运作。在什么情况下他们不会开火?

1 个答案:

答案 0 :(得分:25)

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

什么是ShortCut?

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

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

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

如何指定ShortCut?

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

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

也可以从ApplicationFormApplicationEvents'来解释快捷方式。 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;

如果Handled参数设置为True,则会跳过对该密钥的任何后续处理。

ShortCut是如何捕获的?

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

Peter Below通过VCL写了一篇关于A Key's Odyssey的优秀而全面的文章。简而言之,ShortCut如下:

  • TApplication.Run选择发送到应用程序的每条Windows消息,
  • TApplication.ProcessMessage调用IsKeyMsg,将消息(如果有WM_KEYDOWN消息)传递到焦点控件CN_KEYDOWN消息处理程序。

ShortCut是如何处理的?

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

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

那么,ShortCut何时处理?

当它是:

  • 设置PopupMenu中已启用的MenuItem,该菜单附加到当前焦点控件或其任何父级,
  • 设置MainMenu中已启用的MenuItem,该菜单显示在当前活动的Form或MainForm上,但仅在启用MainForm时显示,
  • 在未暂停的ActionList中设置已启用的操作,该操作列表由当前活动的Form或MainForm拥有,但仅在启用MainForm时,
  • 捕获当前活动表单或MainForm的OnShortCut事件,但仅在启用MainForm时,
  • 捕获Applicaton的OnShortCut事件或任何ApplicationEvents组件。

ShortCut何时未处理?

设置为:

  • 已禁用的MenuItem,
  • 没有菜单的MenuItem,
  • MainMenu中未附加到表单的MenuItem
  • 附加到兄弟姐妹的PopupMenu中的MenuItem
  • 已停用的操作,
  • 没有ActionList的动作,
  • ActionList中的一个Action,它不归当前活动的Form或MainForm所有。例如:另一个表格,数据模块,应用程序,公用事业单位等......
  • ActionList中的一个动作,它不是由当前活动的Form或BDS2006下面的Delphi版本中的MainForm直接拥有。