使用OnKeyDown的Delphi XE和陷印箭头键

时间:2011-12-23 21:17:55

标签: windows delphi winapi delphi-xe

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

5 个答案:

答案 0 :(得分:11)

关键消息由接收这些消息的控件本身处理,这就是为什么当你在按钮上时表单没有收到消息。所以通常你必须对这些控件进行子类化,但是如果表单感兴趣的话,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编辑:要回答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;

答案 1 :(得分:8)

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

如果您希望全面了解按键按键在消息循环中的显示方式,我建议您阅读A Key's Odyssey。如果要在按键成为导航键之前拦截按键,则需要在IsKeyMsg或更早版本中执行此操作。例如,Sertac的answer给出了这样的可能性。

答案 2 :(得分:5)

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

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

以下代码仅在来自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;

答案 3 :(得分:3)

因为他们被抢先去处理将重点放在下一个可用的WinControl上 (我很确定如果你把一个编辑而不是一个按钮,你会看到同样的事情。)

如果您想自己处理它们,可以为Application提供OnMessage事件,该事件将在处理之前对其进行过滤并在那里自行处理。

答案 4 :(得分:0)

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;

即使控件聚焦(按钮,列表框),此代码也能正常工作,因此请注意某些控件可能会丢失其键盘事件(阅读David haffernans的回答)。

具有重点控制的键盘事件

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

添加applicationevent1

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

function KeyboardHookProc

的底部添加以下代码
UnhookWindowsHookEx(KBHook);

并删除

KBHook:=SetWindowsHookEx(WH_KEYBOARD,@KeyboardHookProc, HInstance, 
GetCurrentThreadId());
来自oncreate事件的