Delphi XE и клавиша со стрелкой-ловушкой с OnKeyDown - PullRequest
12 голосов
/ 24 декабря 2011

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

Ответы [ 5 ]

11 голосов
/ 24 декабря 2011

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

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

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

5 голосов
/ 24 декабря 2011

Только объект, имеющий фокус, может получить событие клавиатуры.

Чтобы позволить форме иметь доступ к событию клавиш со стрелками, объявите 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 голосов
/ 24 декабря 2011

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

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

0 голосов
/ 24 декабря 2011
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).

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

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

добавьте 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.

...