ShortCut 什么时候触发? [英] When does a ShortCut fire?
问题描述
昨天我发现了一种情况,即键盘快捷键没有按预期触发.
Yesterday I discovered a situation wherein a keyboard ShortCut did not fire when I was expecting it to.
具体情况是:我在 MDI 子项上按下了 ActionList 的一个 Action 的 ShortCut 组合键,而 MDI 表单上的侧边栏被聚焦.
The specific situation was: I pressed the ShortCut key combination for an Action of an ActionList on an MDI child, while a side bar on the MDI form was focussed.
我一直认为 ShortCuts 可以在全球范围内使用.他们究竟在哪些情况下会开火或不会开火?
I always was under the impression that ShortCuts would work globally. In exactly which circumstances do or do they not fire?
推荐答案
这是一个看似简单的问题,但答案却出奇的长.首先,我将处理一些基础知识,然后通过 VCL 代码按照 ShortCut 进行操作,最终得出 - 我希望 - 一个令人满意的结论.
That's a deceptively simple question with a surprisingly long answer. First I will deal with some basics and then follow the ShortCut through the VCL code to finally arrive at - I hope - a satisfying conclusion.
ShortCut 表示一个或多个引起操作的键的特殊键盘组合.Special 对赋予特定组合键意义的程序员来说是特殊的.
A ShortCut represents a special keyboard combination of one or more keys that cause an operation. Special means special to the programmer who gives meaning to the specific key combination.
在 Delphi 中,快捷方式的类型为 TShortCut
,它被声明为 Word
范围 (0..65535) 内的整数.ShortCut 通常由几个键构成,例如:
In Delphi a ShortCut is of type TShortCut
which is declared as a whole number within the Word
range (0..65535). A ShortCut is often constructed out of several keys, e.g.:
CTRL+K = scCtrl
+ Ord('K')
= 16384
+ 75
= 16459
.
CTRL+K = scCtrl
+ Ord('K')
= 16384
+ 75
= 16459
.
ShortCuts 可以分配给 Action 的 ShortCut
或 SecondaryShortCuts
属性,或者分配给 MenuItem 的 ShortCut
属性,从而调用该 Action 的OnExecute
事件或 MenuItem 的 OnClick
事件,当 ShortCut 的键盘组合被按下时.
ShortCuts can be assigned to the ShortCut
or SecondaryShortCuts
property of an Action or to the ShortCut
property of a MenuItem, thus invoking that Action's OnExecute
event or MenuItem's OnClick
event when the ShortCut's keyboard combination is pressed.
要处理Action 的ShortCut,需要启用Action,并添加到未挂起的ActionList 或附加到启用的MenuItem.同样,要处理MenuItem的ShortCut,需要将MenuItem添加到Menu中.
For an Action's ShortCut to be processed, it is required that the Action is enabled, and added to a not suspended ActionList or attached to an enabled MenuItem. Likewise, for a MenuItem's ShortCut to be processed, it is required that the MenuItem is added to a Menu.
ShortCuts 也可以从 应用程序解释的、表单或来自ApplicationEvents' OnShortCut
事件.在这些事件中,Msg
参数在其 CharCode
成员中保存键码,以及可能的特殊键,如 Shift、Ctrl 或 Alt 可以使用 GetKeyState
:
ShortCuts can also be interpreted from the Application's, a Form's or from an ApplicationEvents' OnShortCut
event. Within those events, the Msg
parameter holds the keycode in its CharCode
member, and possibly special keys like Shift, Ctrl or Alt can be extracted using 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
,将跳过对该键的任何后续处理.
If the Handled
parameter is set to True
, any subsequent processing for the key will be skipped.
VCL 不会保留所有指定快捷方式的列表.(怎么可能?).因此,所有 击键都可能是快捷方式.这正是 VCL 解释 ShortCuts 的方式:通过评估每个按下的键.
The VCL does not keep a list of all specified ShortCuts. (How could it?). Thus all keystrokes could potentially be a ShortCut. And that is exactly how the VCL interprets ShortCuts: by evaluating every key that is pressed.
Peter Above 通过 VCL 撰写了一篇出色而全面的文章,内容涉及A Key's Odyssey.简而言之,ShortCut 被捕获如下:
Peter Below wrote an excellent and comprehensive article dealing with A Key's Odyssey through the VCL. In short, a ShortCut is catched as follows:
TApplication.Run
获取发送到应用程序的每条 Windows 消息,TApplication.ProcessMessage
调用IsKeyMsg
,后者将消息(如果是WM_KEYDOWN
消息)传递到CN_KEYDOWN
焦点控件的消息处理程序.
TApplication.Run
picks up every Windows message send to the application,TApplication.ProcessMessage
callsIsKeyMsg
which passes the message - if aWM_KEYDOWN
message - on to theCN_KEYDOWN
message handler of the focussed Control.
TWinControl.CNKeyDown
检查键是否是菜单键(我们将看到这个menu 的定义超出了物理菜单):
TWinControl.CNKeyDown
examines whether the key is a menu key (we'll see the definition of this menu is beyond a physical Menu):
TWinControl.IsMenuKey
首先检查键是否为控件或其父级 PopupMenu 中的一个快捷方式,TMenu.IsShortCut
1) 遍历所有其(子)菜单项,并使用 ShortCut(如果有)调用已启用 MenuItem 的OnClick
事件处理程序,
TWinControl.IsMenuKey
first examines whether the key is a ShortCut within the Control's or one of its parent's PopupMenu,TMenu.IsShortCut
1) traverses all its (sub)menu items and calls theOnClick
event handler of the enabled MenuItem with the ShortCut, if any,
OnShortCut
窗体的事件 被调用,如果已分配,- 如果未处理,则检查该键是否为 Form 的 MainMenu 中的 ShortCut(参见 1)),如果有,
- 如果未处理,它会将键分派给 Form 拥有的所有 ActionList(仍在讨论活动 Form).在 Delphi 版本 10 (BDS2006) 之前,这些 ActionList 需要由 Form 直接拥有并保存在受保护的(在需要时可以进行干预)字段
FActionLists
中.这被认为是一个错误,从 BDS2006 开始,该领域被淘汰,ActionLists 也可以由 Form 间接拥有.TCustomActionList.IsShortCut
遍历其所有 Action 并调用已启用 Action 的HandleShortCut
,并使用在其ShortCut
或SecondaryShortCuts
属性中设置的 ShortCut,莉>
- The
OnShortCut
event of the Form is called, if assigned, - If not processed, it examines whether the key is a ShortCut within the Form's MainMenu (see 1)), if any,
- If not processed, it dispatches the key to all ActionLists owned by the Form (still talking about the active Form). Prior to Delphi version 10 (BDS2006), these ActionLists need to be directly owned by the Form and were kept in a protected (which made intervention possible when needed) field
FActionLists
. That was considered a bug and as from BDS2006, the field was eliminated and the ActionLists could be indirectly owned by the Form too.TCustomActionList.IsShortCut
traverses all its Actions and callsHandleShortCut
of the enabled Action with the ShortCut set in itsShortCut
orSecondaryShortCuts
property, if any,
OnShortCut
应用程序事件 被触发,这包括OnShortCut
项目中所有 ApplicationEvents 组件的事件,如果有分配的话,- 如果没有被处理,它会调用MainForm的
IsShortCut
例程(见2)),但仅当MainForm已启用.例如.当活动表单是模态表单时,MainForm 将被禁用.这将触发 MainForm 的OnShortCut
事件 或遍历所有直接或间接拥有的 MainForm 的ActionLists(取决于上面提到的 Delphi 版本).
- The
OnShortCut
event of the Application is fired, this includesOnShortCut
events of all ApplicationEvents components within the project, if any assigned, - If not processed, it calls the
IsShortCut
routine of the MainForm (see 2)), but only when the MainForm is enabled. E.g. when the active Form is a modal Form, then the MainForm will be disabled. This will fire theOnShortCut
event of the MainForm or will traverse all directly or indirectly owned ActionLists of the MainForm (depending on Delphi version as mentioned above).
什么时候:
- 在附加到当前焦点控件或其任何父级的 PopupMenu 中设置启用的 MenuItem,
- 在 MainMenu 中设置已启用的 MenuItem,该菜单项显示在当前活动的 Form 或 MainForm 上,但仅在启用 MainForm 时,
- 在当前活动的 Form 或 MainForm 拥有的未挂起的 ActionList 中为启用的 Action 设置,但仅当启用 MainForm 时,
- 在当前活动 Form 或 MainForm 的
OnShortCut
事件中捕获,但仅在启用 MainForm 时, - 在应用程序或任何 ApplicationEvents 组件的
OnShortCut
事件中捕获.
- Set for an enabled MenuItem in a PopupMenu which is attached to the currently focussed Control or to any of its parents,
- Set for an enabled MenuItem in a MainMenu which is shown on the currently active Form or on the MainForm, but only when the MainForm is enabled,
- Set for an enabled Action in a not suspended ActionList which is owned by the currently active Form or by the MainForm, but only when the MainForm is enabled,
- Catched in the
OnShortCut
event of the currently active Form or of the MainForm, but only when the MainForm is enabled, - Catched in the
OnShortCut
event of the Applicaton or any ApplicationEvents components.
设置为:
- 禁用的 MenuItem,
- 没有菜单的 MenuItem,
- MainMenu 中未附加到表单的 MenuItem,
- PopupMenu 中的一个 MenuItem 附加到同级,
- 禁用的操作,
- 没有 ActionList 的 Action,
- ActionList 中不属于当前活动 Form 或 MainForm 的 Action.例如:另一个表单、数据模块、应用程序、实用程序单元等...
- BDS2006 以下 Delphi 版本中,ActionList 中不直接由当前活动的 Form 或 MainForm 拥有的 Action.
这篇关于ShortCut 什么时候触发?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!