R.P Silveira
R.P Silveira

Reputation: 402

OnKeyPress event is't fired when Form has a parent

I'm trying to implement a TDI interface in my project. It's working fine so far (the forms are created inside tabs on my PageControl - as expected). However, I'm facing an annoying issue: the OnKeyPress event isn't fired on my child forms just because they are "parented". I've tried both ways below, but with no success:

procedure TForm1.Button1Click(Sender: TObject);
var
  f: TForm2;
begin
  f := TForm2.Create(self);
  f.ManualDock(PageControl1);
  f.Show;
end;

-OR-

procedure TForm1.Button1Click(Sender: TObject);
var
  f: TForm2;
  tab: TTabSheet;
begin
  tab := TTabSheet.Create(PageControl1);
  tab.PageControl := PageControl1;
  tab.Parent := PageControl1;

  f := TForm2.Create(tab);
  f.BorderStyle := bsNone;
  f.Align := alClient;
  f.Parent := tab;
  tab.Caption := f.Caption;

  f.Show;
end;

¹ needless to say that the KeyPreview property is set as True.

² if I just comment the following line, the event works fine (but the form is not created inside a TabSheet):

//f.Parent := tab;

Has anyone faced this issue before? Any help? Thanks!

Upvotes: 0

Views: 1158

Answers (1)

DNR
DNR

Reputation: 1659

I had a look at the sources, it turns out that the control receiving the KeyPress searches for its parent form, and if the form has KeyPreview set, relays the event to that form:

function TWinControl.DoKeyPress(var Message: TWMKey): Boolean;
var
  Form: TCustomForm;
  Ch: Char;
begin
  Result := True;
  Form := GetParentForm(Self);
  if (Form <> nil) and (Form <> Self) and Form.KeyPreview and
    TWinControl(Form).DoKeyPress(Message) then Exit;
  // [...]
  Result := False;
end;

The parent form is the top level form in the chain. This means that your Form1 receives the event (if it has KeyPreview set) in addition to the original control:

function GetRealParentForm(Control: TControl; TopForm: Boolean = True): TCustomForm;
begin
  while (TopForm or not (Control is TCustomForm)) and (Control.Parent <> nil) do
    Control := Control.Parent;
  if Control is TCustomForm then
    Result := TCustomForm(Control) else
    Result := nil;
end;

This leaves you with a couple of options:

  • Handle the KeyPress in Form1 instead, though you then probably have to find out which child form it came from originally.
  • Set the form's KeyPreview to false, and instead add the handler to the controls on Form2 directly.
  • Modify the VCL source to not relay they KeyPress to the top level form, but instead to the next form in the chain, I think that's what I would do.
  • Of course there might be other options I'm not thinking of right now.

Upvotes: 1

Related Questions