Search code examples
delphidelphi-2010

OnKeyDown not working on Dialog invoked from main form (which also uses OnKeyDown)


Delphi 2010

I am using a OnFormKeyDown event on my main form, and I basically use the same event on a Dialog

//main form

procedure TfrmMain.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
   case Key of
      VK_DOWN: btnLast.OnClick(Self);
      VK_Up:  btnFirst.OnClick(Self);
      VK_Left:  btnPrev.OnClick(Self);
      VK_Right:  btnNext.OnClick(Self);
    end;
end;

procedure TfrmMain.mniShowOwnedClick(Sender: TObject);
var
 I: Integer;
begin
 frmMain.KeyPreview:= False;
 frmOwned.KeyPreview:= True;
 frmOwned.Owned2.Clear;
 for I := 0 to Tags.Count - 1 do
  if Owned.IndexOf(Tags.Names[I]) <> -1 then
     frmOwned.Owned2.Add(Tags[I]);
 if frmOwned.ShowModal = mrOK then
 begin
  frmMain.KeyPreview:= True;
  frmOwned.KeyPreview:= False;
 end;
end;

//Dialog

procedure TfrmOwned.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
   case Key of
      VK_DOWN: btnLast.OnClick(Self);
      VK_Up:  btnFirst.OnClick(Self);
      VK_Left:  btnPrev.OnClick(Self);
      VK_Right:  btnNext.OnClick(Self);
    end;
end;

The Form's OnKeyDown works fine, but I can't seem to get the dialogs to work


Solution

  • The problem is that these keys are used as dialog navigation keys. And as such, they never make their way to the OnKeyDown event.

    To be honest I had a hard time understanding why they are firing for your main form's OnKeyDown event. I could not make that happen in my test environment. That's because I had added a button to the form. That's enough to mean that the arrow keys are treated as navigation keys. Try creating a an app with a single form and adding a few buttons. Then run the app and use the arrow keys to move the focus between the buttons. That's what I mean when I say that the arrow keys are treated as navigation keys.

    I expect that the difference between your two forms is that the main form has nothing that can be navigated around by arrow keys, but the modal form does.

    Now, you could stop the arrow keys being treated as navigation keys. Like this:

    type
      TMyForm = class(TForm)
      ....
      protected
        procedure CMDialogKey(var Message: TCMDialogKey); message CM_DIALOGKEY;
      ....
      end;
    ....
    procedure TMyForm.CMDialogKey(var Message: TCMDialogKey);
    begin
      case Message.CharCode of
      VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN:
        Message.Result := 0;
      else
        inherited;
      end;
    end;
    

    However, a better solution, in my view, is to stop trying to implement shortcuts using OnKeyDown events. That seems like the wrong solution. The right solution is to use actions. Create an action list. Add actions for first, last, previous and next actions. Give them the appropriate ShortCut properties. Assign those actions to your buttons. And the job is done.

    One of the benefits of this is that you can stop trying to fake button click events. For what it is worth, calling OnClick is the wrong way to do that. Call the button's Click method if ever your really need to do that. However, use an action and it's all taken care of.

    Another benefit is that you'll no longer need to pfaff around with KeyPreview. Simply put, if you want to implement short cuts, use TAction.ShortCut.