Delphi XE и клавиша со стрелкой для треппинга с OnKeyDown

Я хочу, чтобы моя форма обрабатывала клавиши со стрелками, и я могу это сделать - пока в форме нет кнопки. Почему это?

Ответ 1

Ключевые сообщения обрабатываются самими элементами управления, которые получают эти сообщения, поэтому, когда вы на кнопке, форма не получает сообщение. Поэтому обычно вам придется подклассифицировать эти элементы управления, но 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;

Ответ 2

Клавиши со стрелками используются для перемещения между кнопками в форме. Это стандартное поведение Windows. Хотя вы можете отключить это стандартное поведение, вы должны подумать дважды, прежде чем идти против стандартного платформы. Клавиши со стрелками предназначены для навигации.

Если вы хотите полностью понизить, как нажатие клавиши находит свой путь через цикл сообщений, я рекомендую читать "Одиссея ключа" , Если вы хотите перехватить нажатие клавиши, прежде чем она станет навигационной клавишей, вам нужно сделать это в IsKeyMsg или ранее. Например, Sertac answer дает такую ​​возможность.

Ответ 3

Только объект с фокусом может принимать событие клавиатуры.

Чтобы форма имела доступ к событию со стрелками, объявите MsgHandler в публичной части формы. В форме create constructor присвойте 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;

Ответ 4

Потому что они вынуждены заниматься настройкой фокуса на следующий доступный WinControl.
(Я уверен, что если вы поместите Edit вместо Button, вы увидите то же самое).

Если вы хотите обработать их самостоятельно, вы можете предоставить Приложению событие OnMessage, которое будет фильтровать их до их обработки и самостоятельно обрабатывать их.

Ответ 5

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 David Haffernans).

события клавиатуры с элементами управления Focused

Например: если у вас есть текстовое поле в вашем приложении и вы хотите получить текст (если сфокусирован), то

добавить приложениеevent1

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.