2011-12-23 16 views
10

Quiero que mi formulario maneje las teclas de flecha, y puedo hacerlo, siempre que no haya ningún botón en el formulario. ¿Por qué es esto?Clave de flecha Delphi XE y Reventado con OnKeyDown

+4

Una gran pregunta. Profundizar en las complejidades subyacentes de cosas como esta es lo que separa a los expertos de los turistas. ¡Seguid así! –

Respuesta

8

Las teclas de flecha se utilizan para navegar entre los botones de un formulario. Este es el comportamiento estándar de Windows. Aunque puede desactivar este comportamiento estándar, debe pensar dos veces antes de ir contra el estándar de la plataforma. Las teclas de flecha están pensadas para la navegación.

Si quiere obtener el máximo de información sobre cómo una pulsación de tecla se abre camino a través del ciclo de mensajes, le recomiendo leer A Key's Odyssey. Si desea interceptar la tecla presionada antes de que se convierta en una tecla de navegación, debe hacerlo en IsKeyMsg o anterior. Por ejemplo, el answer de Sertac ofrece una de esas posibilidades.

+0

Peter Below (autor de Key's Odyssey) es un importante miembro de la comunidad Delphi (TeamB) y un gran colaborador de JVCL. Buen articulo. –

3

Debido a que están prevenidos para tratar de establecer el foco en el próximo WinControl disponible.
(estoy bastante seguro de que si pones un Editar en lugar de un Botón, verás lo mismo).

Si desea manejarlos usted mismo, puede proporcionar a la Aplicación un evento OnMessage que filtre esos antes de que se procesen y los maneje usted mismo allí.

10

Los mensajes clave son procesados ​​por los propios controles que reciben estos mensajes, es por eso que cuando está en un botón el formulario no recibe el mensaje. Por lo que normalmente tendría que subclase estos controles, pero la VCL es tan amable de pedir a la forma de crianza qué hacer si el formulario está interesado:

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 de edición: para contestar la OP pregunta original, necesita llamar aKeyDown de alguna manera para que su código de evento funcione (siéntase libre de editar; era demasiado largo para un comentario).

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

Creo que necesitas un poco más de explicación en cuanto a lo especial sobre 'CM_DIALOGKEY'. Esta es sin duda la forma elegante de cambiar el comportamiento. Siéntase libre de superponerse con mi respuesta y la eliminaré, ya que una respuesta completa con este código es mejor que lo que hice. –

+0

@David - Me temo que no podrás borrar tu publicación :). No hay mucho que pueda explicar sobre CM_DIALOGKEY, es un mensaje compuesto de VCL usado para transmitir mensajes clave. Esto es lo que dice mi archivo de ayuda al respecto: * "Esto es constante Controls.CM_DIALOGKEY" * :). Actualización: La [documentación] (http://docwiki.embarcadero.com/VCL/2010/en/Controls.CM_DIALOGKEY) ha sido mejorada: * "CM_DIALOGKEY representa un mensaje de control y es usado internamente por el marco VCL." * –

+0

Hmm, pensé que sonaba como si estuviera relacionado con 'TApplication.IsDlgMsg'. De hecho es '' TApplication.IsDlgMsg' incluso relevante aquí o la VCL maneja estas teclas de navegación en lugar de los diálogos de Windows? –

5

Solo el objeto que tiene el foco puede recibir un evento de teclado.

Para que el formulario tenga acceso al evento de teclas de flecha, declare MsgHandler en la parte pública del formulario. En la forma crear constructor, asigne Application.OnMessage a este MsgHandler.

El siguiente código intercepta las teclas de flecha solo si provienen de un descendiente TButton. Se pueden agregar más controles según sea necesario.

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; 

Este código funcionará incluso cuando un control se centra (botones, cuadros de lista), así que ten cuidado algunos controles pueden perder sus eventos de teclado (Leer respuesta David haffernans).

eventos de teclado con controles enfocados

por ejemplo: Si usted está teniendo cuadro de texto en su aplicación y desea recive texto (si se centró) también, entonces

añadir 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; 

agregue el siguiente código en la parte inferior de function KeyboardHookProc

UnhookWindowsHookEx(KBHook); 

y quitar

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

del evento alcrear.

Cuestiones relacionadas