2011-12-23 55 views
10

我希望我的表單處理箭頭鍵,我可以這樣做 - 只要表單上沒有按鈕。爲什麼是這樣?Delphi XE和陷印箭頭鍵與OnKeyDown

+4

偉大的問題。深入挖掘像這樣的事物的複雜性,將專家與遊客分開。保持! – 2011-12-24 20:40:47

回答

8

箭頭鍵用於在窗體上的按鈕之間導航。這是標準的Windows行爲。雖然您可以禁用此標準行爲,但在與平臺標準相違背之前,您應該考慮三次。箭頭鍵用於導航。

如果您想充分了解按鍵在消息循環中的位置,我建議您閱讀A Key's Odyssey。如果您想在成爲導航鍵之前攔截按鍵,則需要在IsKeyMsg或更早版本中進行。例如,Sertac的answer給出了這樣一種可能性。

+0

彼得下面(該密鑰的奧德賽的作者)是一個重要的德爾福社區人員(TeamB),也是一個偉大的JVCL貢獻者。好文章。 – 2011-12-24 20:37:54

3

因爲他們被搶先處理將重點放在下一個可用的WinControl上。
(我敢肯定,如果你把一個編輯而不是一個按鈕,你會看到同樣的東西)。

如果你想自己處理它們,你可以給應用程序提供一個OnMessage事件,它將在它們被處理之前對它們進行過濾,並在那裏自己處理它們。

10

關鍵消息由接收這些消息的控件自己處理,這就是爲什麼當您在按鈕上時表單沒有收到消息。因此,一般來說,你將不得不繼承這些控件,但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; 

弗朗索瓦編輯:回答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; 
+0

我想你需要更多的解釋來說明一下'CM_DIALOGKEY'的特殊之處。這當然是改變行爲的狡猾方式。隨意與我的答案重疊,我會刪除它,因爲這個代碼的全面答案勝過我所做的。 – 2011-12-23 22:06:37

+0

@大衛 - 恐怕你將無法刪除你的帖子:)。關於CM_DIALOGKEY我沒有太多的解釋,這是一個VCL組成的消息,用於廣播關鍵消息。這是我的幫助文件所說的:*「這是常數Controls.CM_DIALOGKEY」* :)。更新:[文檔](http://docwiki.embarcadero.com/VCL/2010/en/Controls.CM_DIALOGKEY)已得到改進:*「CM_DIALOGKEY表示控制消息,由VCL框架在內部使用。」* – 2011-12-23 22:16:13

+0

嗯,我認爲這聽起來好像與「TApplication.IsDlgMsg」有關。事實上,'TApplication.IsDlgMsg'在這裏甚至是相關的,或者VCL是否處理這些導航鍵而不是Windows對話框? – 2011-12-23 22:24:38

5

只有具有焦點的對象才能接收鍵盤事件。

爲了讓表單有權訪問箭頭鍵事件, 在表單的公共部分聲明MsgHandler。 在表單create構造函數中,將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; 
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答案)。

與聚焦控制

鍵盤事件例如:如果你在你的應用程序的文本框,並希望recive文本(如果聚焦)也,然後

添加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); 

,並從OnCreate事件刪除

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