Jan Doggen
Jan Doggen

Reputation: 9096

How to capture KeyDown when focused controls interfere?

I have a form with KeyPreview=true and want to capture the arrow keys, unless we are in a control that should handle those. The issue is: focus is always on one of those controls.

How can I adapt/design this to work?

.PAS file

unit uKeyDownTests;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TFrmKeyDownTests = class(TForm)
    PnlBottom: TPanel;
    PnlClient: TPanel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  private
  public
  end;

var
  FrmKeyDownTests: TFrmKeyDownTests;

implementation

{$R *.dfm}

type
  THackWinControl = class(TWinControl);

procedure TFrmKeyDownTests.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var lActiveControl: TControl;
begin
   // Earlier code, but that did not work either:
   // if Edit1.Focused or Edit2.Focused or Edit3.Focused then Exit;

   lActiveControl := ActiveControl;
   if Assigned(lActiveControl) then
   begin
      if lActiveControl = Edit1 then
      begin
          THackWinControl(Edit1).KeyDown(Key,Shift);
          Exit;
      end;
      if lActiveControl = Edit2 then
      begin
          THackWinControl(Edit2).KeyDown(Key,Shift);
          Exit;
      end;
      if lActiveControl = Edit3 then
      begin
          THackWinControl(Edit3).KeyDown(Key,Shift);
          Exit;
      end;
   end;

   if (Key = VK_RIGHT) then
   begin
      PnlBottom.Caption := PnlBottom.Caption + 'R';
      Key := 0;
      Exit;
   end;
   if (Key = VK_LEFT) then
   begin
      PnlBottom.Caption := PnlBottom.Caption + 'L';
      Key := 0;
      Exit;
   end;
   if (Key = VK_UP) then
   begin
      PnlBottom.Caption := PnlBottom.Caption + 'U';
      Key := 0;
      Exit;
   end;
   if (Key = VK_DOWN) then
   begin
      PnlBottom.Caption := PnlBottom.Caption + 'D';
      Key := 0;
      Exit;
   end;
end;

end.

.DFM file

object FrmKeyDownTests: TFrmKeyDownTests
  Left = 0
  Top = 0
  Caption = 'Keydown tests'
  ClientHeight = 336
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  KeyPreview = True
  OldCreateOrder = False
  OnKeyDown = FormKeyDown
  PixelsPerInch = 96
  TextHeight = 13
  object PnlBottom: TPanel
    Left = 0
    Top = 295
    Width = 635
    Height = 41
    Align = alBottom
    TabOrder = 0
  end
  object PnlClient: TPanel
    Left = 0
    Top = 0
    Width = 635
    Height = 295
    Align = alClient
    TabOrder = 1
    object Edit1: TEdit
      Left = 40
      Top = 32
      Width = 121
      Height = 21
      TabOrder = 0
      Text = 'Edit1'
    end
    object Edit2: TEdit
      Left = 40
      Top = 72
      Width = 121
      Height = 21
      TabOrder = 1
      Text = 'Edit1'
    end
    object Edit3: TEdit
      Left = 40
      Top = 112
      Width = 121
      Height = 21
      TabOrder = 2
      Text = 'Edit1'
    end
  end
end

Upvotes: 1

Views: 1374

Answers (1)

Jan Doggen
Jan Doggen

Reputation: 9096

(Answering my own question for my specific situation, slightly different from the one in the 'Possible dupe', but based on the answers there)
In my case, the easiest solution was:

  • procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY; which only calls inherited
  • KeyPreview=true for the form
  • A FormKeydown that handles what I want to do with arrow keys

Result:

  • The controls that have focus as well as the form handle the arrow keys
  • It does not matter if the controls have an OnKeyDown handler (the Edit2 control) or not (the others)

Modified code:

unit uKeyDownTests;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TFrmKeyDownTests = class(TForm)
    PnlBottom: TPanel;
    PnlClient: TPanel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure Edit2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  private
    procedure DialogKey(var Msg: TWMKey); message CM_DIALOGKEY;  public
  end;

var
  FrmKeyDownTests: TFrmKeyDownTests;

implementation

{$R *.dfm}

procedure TFrmKeyDownTests.DialogKey(var Msg: TWMKey);
begin
   inherited;
end;

procedure TFrmKeyDownTests.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
   case Key of
      VK_RIGHT: PnlBottom.Caption := PnlBottom.Caption + 'R';
      VK_LEFT : PnlBottom.Caption := PnlBottom.Caption + 'L';
      VK_UP   : PnlBottom.Caption := PnlBottom.Caption + 'U';
      VK_DOWN : PnlBottom.Caption := PnlBottom.Caption + 'D';
   end;
end;


{ TFrmKeyDownTests }

procedure TFrmKeyDownTests.Edit2KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
   PnlBottom.Caption := PnlBottom.Caption + '-kd-';
end;

end.

Upvotes: 1

Related Questions