windows Delphi XE 和带 OnKeyDown 的捕获箭头键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8620703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 18:43:53  来源:igfitidea点击:

Delphi XE and Trapping Arrow Key with OnKeyDown

windowsdelphiwinapidelphi-xe

提问by Jeff Bannon

I want my form to handle the arrow keys, and I can do it -- as long as there is no button on the form. Why is this?

我想让我的表单处理箭头键,我可以做到——只要表单上没有按钮。为什么是这样?

回答by Sertac Akyuz

Key messages are processed by the controls themselves who receives these messages, that's why when you're on a button the form is not receiving the message. So normally you would have to subclass these controls, but the VCL is kind enough to ask the parenting form what to do if the form is interested:

关键消息由接收这些消息的控件本身处理,这就是为什么当你在一个按钮上时,表单没有接收到消息。所以通常你必须对这些控件进行子类化,但 VCL 很友好地询问父母表单如果表单感兴趣该怎么做:

type
  TForm1 = class(TForm)
    ..
  private
    procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY;
    ..


procedure TForm1.DialogKey(var Msg: TWMKey); 
begin
  if not (Msg.CharCode in [VK_DOWN, VK_UP, VK_RIGHT, VK_LEFT]) then
    inherited;
end;

Fran?ois editing:to answer the OP original question, you need to call onKeyDown somehow so that his event code would work (feel free to edit; was too long for a comment).

Fran?ois 编辑:要回答 OP 原始问题,您需要以某种方式调用 onKeyDown 以便他的事件代码可以工作(随意编辑;评论太长)。

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  private
    { Private declarations }
    procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.DialogKey(var Msg: TWMKey);
begin
  case Msg.CharCode of
    VK_DOWN, VK_UP, VK_RIGHT, VK_LEFT:
      if Assigned(onKeyDown) then
        onKeyDown(Self, Msg.CharCode, KeyDataToShiftState(Msg.KeyData));
    else
      inherited
  end;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case Key of
    VK_DOWN: Top := Top + 5;
    VK_UP: Top := Top - 5;
    VK_LEFT: Left := Left - 5;
    VK_RIGHT: Left := Left + 5;
  end;
end;

回答by David Heffernan

Arrow keys are used to navigate between buttons on a form. This is standard Windows behaviour. Although you can disable this standard behaviour you should think twice before going against the platform standard. Arrow keys are meant for navigation.

箭头键用于在表单上的按钮之间导航。这是标准的 Windows 行为。尽管您可以禁用此标准行为,但在违反平台标准之前应该三思而后行。箭头键用于导航。

If you want to get the full low down on how a key press finds its way through the message loop I recommend reading A Key's Odyssey. If you want to intercept the key press before it becomes a navigation key, you need to do so in IsKeyMsgor earlier. For example, Sertac's answergives one such possibility.

如果您想全面了解按键如何通过消息循环找到它的方式,我建议您阅读A Key 的 Odyssey。如果你想在按键变成导航键之前拦截它,你需要在IsKeyMsg或更早的时候这样做。例如,Sertac 的回答给出了一种这样的可能性。

回答by LU RD

Only the object that has the focus can receive a keyboard event.

只有获得焦点的对象才能接收键盘事件。

To let the form have access to the arrow keys event, declare a MsgHandlerin the public part of the form. In the form create constructor, assign the Application.OnMessageto this MsgHandler.

要让表单可以访问箭头键事件,请MsgHandler在表单的公共部分声明 a 。在窗体创建构造函数中,将 分配Application.OnMessage给此 MsgHandler。

The code below intercepts the arrow keys only if they are coming from a TButton descendant. More controls can be added as needed.

下面的代码仅在方向键来自 TButton 后代时才拦截它们。可以根据需要添加更多控件。

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnMessage := Self.MsgHandler;
end;

procedure TForm1.MsgHandler(var Msg: TMsg; var Handled: Boolean);
var
  ActiveControl: TWinControl;
  key : word;
begin
  if (Msg.message = WM_KEYDOWN) then
    begin
      ActiveControl := Screen.ActiveControl;
      // if the active control inherits from TButton, intercept the key.
      // add other controls as fit your needs 
      if not ActiveControl.InheritsFrom(TButton)
        then Exit;

      key := Msg.wParam;
      Handled := true;
      case Key of // intercept the wanted keys
        VK_DOWN : ; // doStuff
        VK_UP : ; // doStuff
        VK_LEFT : ; // doStuff
        VK_RIGHT : ; // doStuff
        else Handled := false;
      end;
   end;
end;

回答by Fran?ois

Because they are preempted to deal with setting the focus on the next available WinControl.
(I'm pretty sure that if you put an Edit instead of a Button you see the same thing).

因为它们被抢占处理设置焦点在下一个可用的 WinControl 上。
(我很确定如果你放置一个 Edit 而不是 Button 你会看到同样的东西)。

If you want to handle them yourself, you can provide the Application with an OnMessage event that will filter those before they are processed and handle them yourself there.

如果你想自己处理它们,你可以为应用程序提供一个 OnMessage 事件,该事件将在处理它们之前过滤它们并在那里自己处理它们。

回答by VibeeshanRC

var
KBHook: HHook; {this intercepts keyboard input}

implementation

{$R *.dfm}

function KeyboardHookProc(Code: Integer; WordParam: Word; LongParam: LongInt): LongInt; stdcall;
 begin
 case WordParam of
   vk_Space: ShowMessage ('space')  ;
   vk_Right:ShowMessage ('rgt') ;
   vk_Left:ShowMessage ('lft') ;
   vk_Up: ShowMessage ('up') ;
   vk_Down: ShowMessage ('down') ;
  end; {case}
 end;

procedure TForm4.FormCreate(Sender: TObject);
begin
KBHook:=SetWindowsHookEx(WH_KEYBOARD,@KeyboardHookProc,HInstance,GetCurrentThreadId());
end;

This code will work even when a control is focused (buttons , listboxes), so be careful some controls may loose their keyboard events (Read David haffernans answer) .

即使当一个控件被聚焦(按钮、列表框)时,这段代码也能工作,所以要小心一些控件可能会丢失它们的键盘事件(阅读 David haffernans 的回答)。

keyboard events with Focused controls

带有焦点控件的键盘事件

eg: If you are having textbox in your app and want to recive text(if focused) also , then

例如:如果您的应用程序中有文本框并且还想接收文本(如果有焦点),那么

add an applicationevent1

添加一个应用程序事件1

procedure TForm4.ApplicationEvents1Message(var Msg: tagMSG;var Handled: Boolean);
begin
if Msg.message = WM_KEYFIRST then
  KBHook:=SetWindowsHookEx(WH_KEYBOARD,@KeyboardHookProc,HInstance,GetCurrentThreadId());
end;

add the following code at the bottom of the function KeyboardHookProc

在底部添加以下代码 function KeyboardHookProc

UnhookWindowsHookEx(KBHook);

and remove

并删除

KBHook:=SetWindowsHookEx(WH_KEYBOARD,@KeyboardHookProc, HInstance, 
GetCurrentThreadId());

from oncreate event.

来自 oncreate 事件。