如何避免重复执行TAction.Shortcut?

How to avoid repeating execution of TAction.Shortcut?

在 Delphi 10.1.2 中,在 TActionList 中,我创建了一个具有这些属性的 TAction 并为其分配了一个快捷方式 Ctrl+F12

在运行时,当我按住快捷键Ctrl+F12时,动作会重复执行(速度取决于系统键盘重复速度)。

那么即使用户一直按下这些键或者如果用户的系统具有较高的键盘重复速度设置,我怎样才能使操作只执行一次(直到这些键被抬起)?

您可以使用 SystemParametersInfo 检索系统键盘设置。 SPI_GETKEYBOARDDELAY 给出重复延迟;第一次和第二次生成事件之间的时间。 SPI_GETKEYBOARDSPEED 给出键盘重复率;初始延迟后事件之间的持续时间。该文档具有最慢和最快设置的近似值,可以帮助您确定作用点。

假设你决定采取行动。由于快捷方式与生成它们的事件没有直接关系,因此它们没有 属性 或任何可以揭示它是否是 initialdelayed 的信息,或重复执行。

然后,一种选择是在输入快捷方式后立即禁用该快捷方式的执行,并在释放相应的键后重新启用。您必须将表单的 KeyPreview 设置为 true 才能实施此解决方案,因为表单上的任何控件都可能是生成快捷方式时具有焦点的控件。

我会发现一个不太麻烦的解决方案是在不是由初始按键生成时阻止生成快捷方式。这次你得注意按键事件了。

一种可能的实现方式是安装本地键盘挂钩。

var
  KeybHook: HHOOK;

procedure TForm1.FormCreate(Sender: TObject);
begin
  KeybHook := SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, 0, GetCurrentThreadId);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  UnhookWindowsHookEx(KeybHook);
end;

在回调中测试感兴趣的键的重复次数可能很诱人,但是,正如 WM_KEYDOWN 的文档中所述,重复次数不是累积的。这实际上意味着 OS 不提供此信息。虽然提供了以前的关键状态 information。那将是回调的 "lParam" 的第 30 位。当您的快捷键按下时,您可以防止任何键盘消息到达焦点控件的 window 过程。

function KeyboardProc(code: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT;
    stdcall;
begin
  if code > 0 then begin
    if (wParam = vk_f12) and (GetKeyState(VK_CONTROL) < 0) and
        ((lParam and 000000) > 0) then begin
      Result := 1;
      Exit;
    end;
  end;
  Result := CallNextHookEx(KeybHook, code, wParam, lParam);
end;


最后,如果用户的系统设置了快速键盘重复,请不要忽视这种可能性,这是用户的选择而不是不选择。