我希望我的表单处理箭头键,我可以这样做 - 只要表单上没有按钮。这是为什么?
答案 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事件的。