ShortCut什么时候开火? [英] When does a ShortCut fire?
问题描述
昨天我发现一个情况,一个键盘ShortCut在我期待的时候没有触发。
具体情况是:我按下ShortCut组合键而MDI窗体上的一个侧边栏则集中在MDI子窗口上的ActionList操作。
我总是觉得ShortCuts会在全球范围内工作。
这是一个非常简单的问题,令人惊讶的是长时间的答案。首先,我将处理一些基础知识,然后通过VCL代码跟随ShortCut终于到达 - 希望 - 一个令人满意的结论。
什么是ShortCut? h3>
ShortCut表示导致操作的一个或多个键的特殊键盘组合。 在 Word
范围(0..65535)中声明为整数的TShortCut 。 ShortCut通常由几个键构成,例如:
CTRL + K = scCtrl
+ Ord('K')
= 16384
+ 75
= 16459
。
如何指定ShortCut?
ShortCuts可以分配给一个Action的 ShortCut
或 SecondaryShortCuts
属性或$菜单项的 ShortCut
属性,从而调用Action的 OnExecute
事件或MenuItem的 OnClick
事件当ShortCut的键盘组合被按下。
对于要处理的Action的ShortCut,需要启用Action,并添加到未挂起的ActionList或附加到启用的MenuItem。同样,对于要处理的MenuItem的ShortCut,需要将MenuItem添加到菜单。
ShortCuts也可以从应用程序的一个表单或 ApplicationEvents ' OnShortCut
事件。在这些事件中, Msg
参数在其 CharCode
成员中保存键码,可能是特殊键,如 Shift , Ctrl 或 Alt 可以使用 GetKeyState
:
procedure TForm1.FormShortCut(var Msg:TWMKey; var Handled:Boolean);
begin
if(Msg.CharCode = Ord('K'))和(GetKeyState(VK_CONTROL)< 0)then
begin
Caption:='CTRL + ';
处理:= True;
结束
结束
如果处理
参数设置为 True
,任何后续的密钥处理将被跳过。
ShortCut如何捕获?
VCL不保留所有指定的ShortCuts的列表。 (怎么可能?)因此,所有的击键可能是ShortCut。这就是VCL如何解释ShortCuts:通过评估按下的每个键。
Peter下面写了一篇非常全面的文章,与 A Key's Odyssey 通过VCL。简而言之,ShortCut被捕获如下:
-
TApplication.Run
每个Windows消息发送到应用程序, -
TApplication.ProcessMessage
调用IsKeyMsg
它传递消息 - 如果WM_KEYDOWN
消息发送到重点控件的CN_KEYDOWN
消息处理程序/ strong>。
如何处理ShortCut?
TWinControl.CNKeyDown
检查键是否是菜单键(我们将看到这个菜单的定义超出了物理菜单):
-
TWinControl.IsMenuKey
首先检查密钥是否是控件内的ShortCut或其父项的 PopupMenu 之一,
- < a href =http://docwiki.embarcadero.com/Libraries/XE7/en/所有的全部它的(子类别)
TMenu.IsShortCut
)菜单项,并调用启用的MenuItem的OnClick
事件处理程序,使用ShortCut(如果有)
- < a href =http://docwiki.embarcadero.com/Libraries/XE7/en/所有的全部它的(子类别)
- 如果未处理,则会通过调用 2),
-
OnShortCut
- 如果没有处理,表单的事件将被调用,它会检查密钥是否是窗体的MainMenu中的ShortCut strong>(见 1)),如果有的话,
- 如果没有处理,它会将密钥发送到Form ActionLists (还在谈论活跃的For m)。在Delphi版本10(BDS2006)之前,这些ActionLists需要由Form直接拥有,并保存在受保护的(在需要时可以进行干预)字段
FActionLists
。 这被认为是一个错误,从BDS2006开始,该领域被淘汰,ActionLists可以间接由表格也拥有。
-
TCustomActionList.IsShortCut
遍历其所有操作,并调用中设置的ShortCut启用的Action的
或HandleShortCut
ShortCutSecondaryShortCuts
属性(如果有)
-
-
- 如果未处理,
Application.IsShortCut
被调用(通过CM_APPKEYDOWN
),
-
OnShortCut
应用程序事件被触发,其中包括OnShortCut
项目中所有ApplicationEvents组件的事件,如果已分配, - 如果未处理,它会调用 MainForm 的
IsShortCut
例程(请参阅 2)),但只有当MainForm启用时才能。例如。当活动窗体为模态窗体时,MainForm将被禁用。这将触发MainForm 的OnShortCut
事件,或者将遍历MainForm 的所有直接或间接拥有的 ActionLists(依赖于在上面提到的Delphi版本上)。
-
那么, ?
当它是:
- 在PopupMenu中设置启用的MenuItem它附加到当前聚焦的控件或其任何父母,
- 为当前活动窗体或MainForm上显示的MainMenu中的启用MenuItem设置,但仅当MainForm已启用,
- 设置为在当前活动的窗体或MainForm所有的未挂起的ActionList中的启用的操作,但仅当启用MainForm时,才能设置
- 在当前活动的Form或MainForm的
OnShortCut
事件中捕获,但只有启用MainForm时, - 在
OnShortCut
应用程序或任何ApplicationEvents组件的事件。
什么时候是ShortCut未处理?
设置为:
- 禁用的MenuItem,
- 没有菜单的MenuItem
- MainMenu中没有附加到表单的MenuItem
- 附加到兄弟姐妹的PopupMenu中的MenuItem
- 禁用的操作
- 没有ActionList的操作
- ActionList中不是当前活动窗体或MainForm所有的Action。例如:另一个表单,DataModule,应用程序,公用程序单元等...
- ActionList中的Action并不直接由Delphi版本中当前活动的Form或MainForm所有低于BDS2006。
Yesterday I discovered a situation wherein a keyboard ShortCut did not fire when I was expecting it to.
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.
I always was under the impression that ShortCuts would work globally. In exactly which circumstances do or do they not fire?
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.
What is a ShortCut?
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.
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
.
How to specify a 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.
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 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;
If the Handled
parameter is set to True
, any subsequent processing for the key will be skipped.
How is a ShortCut catched?
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 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
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.
How is the ShortCut processed?
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
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,
- If not processed, it examines whether the key is a ShortCut of the Form on which the Control resides by calling
TCustomForm.IsShortCut
2),- 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,
- The
- If not processed,
Application.IsShortCut
is called (viaCM_APPKEYDOWN
),- 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).
- The
So, when is the ShortCut processed?
When it is:
- 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.
And when is the ShortCut not processed?
When it is set for:
- A disabled MenuItem,
- A MenuItem without Menu,
- A MenuItem in a MainMenu that is not attached to a Form,
- A MenuItem in a PopupMenu that is attached to a sibling,
- A disabled Action,
- An Action without ActionList,
- An Action in an ActionList that is not owned by the currently active Form or the MainForm. E.g.: another Form, a DataModule, Application, a utilities unit, etc...
- An Action in an ActionList that is not directly owned by the currently active Form or the MainForm in Delphi versions below BDS2006.
这篇关于ShortCut什么时候开火?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!