2011-12-23 9 views
10

Desidero che il mio modulo gestisca i tasti freccia e posso farlo, a condizione che sul modulo non sia presente alcun pulsante. Perchè è questo?Delphi XE e tasto freccia Trapping con OnKeyDown

+4

Ottima domanda. Scavare nella complessità sottostante di cose come questa è ciò che separa gli esperti dai turisti. Continuate così! –

risposta

8

I tasti freccia consentono di spostarsi tra i pulsanti di un modulo. Questo è il comportamento standard di Windows. Sebbene tu possa disabilitare questo comportamento standard, dovresti pensarci due volte prima di andare contro lo standard della piattaforma. I tasti freccia sono pensati per la navigazione.

Se si desidera ottenere il massimo in basso su come un tasto stampa trova la sua strada attraverso il ciclo dei messaggi, mi raccomando di leggere A Key's Odyssey. Se si desidera intercettare la pressione del tasto prima che diventi un tasto di navigazione, è necessario farlo in IsKeyMsg o precedente. Ad esempio, Seamac answer offre una tale possibilità.

+0

Peter Below (autore di quella Key's Odyssey) è un importante ragazzo della comunità Delphi (TeamB) e un grande collaboratore di JVCL. Bell'articolo –

3

Poiché sono preempiti per occuparsi di impostare l'attenzione sul prossimo WinControl disponibile.
(Sono abbastanza sicuro che se si inserisce una modifica anziché un pulsante si vede la stessa cosa).

Se si desidera gestirli autonomamente, è possibile fornire all'applicazione un evento OnMessage che filtrerà quelli prima che vengano elaborati e li gestisca da soli.

10

I messaggi chiave vengono elaborati dai controlli stessi che ricevono questi messaggi, ecco perché quando si è su un pulsante il modulo non riceve il messaggio. Quindi normalmente si dovrà sottoclasse questi controlli, ma la VCL è così gentile da chiedere il modulo di genitorialità che cosa fare se la forma è interessato:

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: per rispondere alla OP domanda iniziale, è è necessario chiamare onKeyDown in qualche modo in modo che il suo codice evento funzioni (sentitevi liberi di modificare, era troppo lungo per un commento).

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

Penso che tu abbia bisogno di un po 'di spiegazioni su cosa sia speciale su 'CM_DIALOGKEY'. Questo è sicuramente il modo perfetto per cambiare comportamento. Sentiti libero di sovrapporsi alla mia risposta e la cancellerò poiché una risposta esauriente con questo codice in battute è ciò che ho fatto. –

+0

@David - Temo che non sarai in grado di cancellare il tuo post :). Non c'è molto che possa spiegare su CM_DIALOGKEY, è un messaggio composto VCL usato per trasmettere messaggi chiave. Questo è ciò che il mio file di aiuto dice a riguardo: * "Questo è costante Controls.CM_DIALOGKEY" * :). Aggiornamento: La [documentazione] (http://docwiki.embarcadero.com/VCL/2010/en/Controls.CM_DIALOGKEY) è stata migliorata: * "CM_DIALOGKEY rappresenta un messaggio di controllo ed è utilizzato internamente dal framework VCL." * –

+0

Hmm, ho pensato che suonasse come se fosse collegato a 'TApplication.IsDlgMsg'. Infatti, 'TApplication.IsDlgMsg' è rilevante anche qui o il VCL gestisce questi tasti di navigazione piuttosto che le finestre di dialogo di Windows? –

5

Solo l'oggetto che ha il focus può ricevere un evento di tastiera.

Per consentire al modulo di accedere agli eventi dei tasti di direzione, dichiarare un MsgHandler nella parte pubblica del modulo. Nel modulo create constructor, assegnare Application.OnMessage a questo MsgHandler.

Il codice seguente intercetta i tasti freccia solo se provengono da un discendente TButton. Più controlli possono essere aggiunti secondo necessità.

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; 

Questo codice funzionerà anche quando un controllo si concentra (pulsanti, caselle di riepilogo), quindi fate attenzione alcuni controlli potrebbero perdere i loro eventi di tastiera (Leggi David haffernans risposta).

eventi della tastiera con controlli mirati

esempio: Se si hanno di testo nella vostra app e Desidero ricevere il testo (se concentrata) anche, quindi

aggiungere un 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; 

aggiungere il seguente codice nella parte inferiore dello function KeyboardHookProc

UnhookWindowsHookEx(KBHook); 

e rimuovere

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

dall'evento OnCreate.

Problemi correlati