【问题标题】:When does a ShortCut fire?快捷方式何时触发?
【发布时间】:2015-01-20 21:23:59
【问题描述】:

昨天我发现了一种情况,键盘快捷键在我预期的时候没有触发。

具体情况是:我在MDI子上按下了ActionList的Action的ShortCut组合键,而MDI窗体上的侧边栏被聚焦。

我一直认为 ShortCuts 可以在全球范围内使用。他们究竟在哪些情况下开火或不开火?

【问题讨论】:

  • 你能准确地说一下侧边栏是什么吗?
  • 我猜重要的是它在 mdi 表单中的位置。
  • 根据答案,鉴于活动表单有一个 ActionList,应该按照接受的答案触发快捷方式,除非该操作以某种方式被禁用。是这样吗?
  • @costa MDI 子窗体未激活,MDI 主窗体已激活。

标签: delphi keyboard-shortcuts vcl


【解决方案1】:

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

什么是快捷方式?

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

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

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

如何指定快捷方式?

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

要处理 Action 的 ShortCut,需要启用该 Action,并将其添加到未暂停的 ActionList 或附加到已启用的 MenuItem。同样,要处理 MenuItem 的 ShortCut,需要将 MenuItem 添加到 Menu。

ShortCut 也可以从 Application's、Form's 或 ApplicationEvents'OnShortCut 事件中解释。在这些事件中,Msg 参数在其CharCode 成员中保存键码,以及可能的特殊键,如 ShiftCtrlAlt可以使用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,则将跳过对该密钥的任何后续处理。

快捷方式是如何捕获的?

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

Peter below 写了一篇优秀而全面的文章,通过 VCL 处理A Key's Odyssey。简而言之,一个 ShortCut 被捕获如下:

  • TApplication.Run 接收发送到应用程序的每条 Windows 消息,
  • TApplication.ProcessMessage 调用 IsKeyMsg 将消息(如果是 WM_KEYDOWN 消息)传递给 focused ControlCN_KEYDOWN 消息处理程序。

ShortCut 是如何处理的?

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

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

那么,ShortCut 什么时候处理?

什么时候:

  • 为 PopupMenu 中启用的 MenuItem 设置,该菜单项附加到当前聚焦的 Control 或其任何父级,
  • 为 MainMenu 中启用的 MenuItem 设置,该菜单项显示在当前活动的 Form 或 MainForm 上,但仅在启用 MainForm 时,
  • 为当前活动表单或 MainForm 拥有的未暂停 ActionList 中启用的 Action 设置,但仅当 MainForm 启用时,
  • 在当前活动窗体或 MainForm 的 OnShortCut 事件中捕获,但仅在启用 MainForm 时,
  • 在 Applicaton 或任何 ApplicationEvents 组件的 OnShortCut 事件中捕获。

什么时候没有处理快捷方式?

当它被设置为:

  • 禁用的 MenuItem,
  • 没有菜单的 MenuItem,
  • MainMenu 中未附加到表单的 MenuItem,
  • PopupMenu 中附加到同级的 MenuItem,
  • 已禁用的操作,
  • 没有 ActionList 的动作,
  • ActionList 中的一个 Action,不属于当前活动的 Form 或 MainForm。例如:另一个表单、数据模块、应用程序、实用程序单元等...
  • ActionList 中的一个 Action,不直接由当前活动的 Form 或 BDS2006 以下 Delphi 版本中的 MainForm 所有。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-04
    • 1970-01-01
    • 2019-12-03
    • 1970-01-01
    • 2015-01-09
    相关资源
    最近更新 更多