Author: Jonas Bilinkevicius
I have a TabControl inserted into a MDI child form. When one press the Ctrl+Tab or
Ctrl+Shift+Tab keys, the application selects the next (previous) MDIChildForm
instead of changing the active page of TabControl. How can I force the MDIChild
pass the Ctrl+Tab to the TabControl?
Answer:
This is in fact a conflict on the API level. In a MDI application the message loop
will call IsMDIMsg on every key message fetched form the message loop, and this
function calls the API TranslateMDISysAccel function. This in turn handles the
Ctrl-Tab, so the child form never even sees the key event.
To get around this one needs to intervene before IsMDIMsg is even called. There is
only one opportunity to do this: the Application.OnMessage event. So add a handler
for the main form OnCreate event, and add a private method to the form called
AppMessage:
1 procedure TMainForm.FormCreate(Sender: TObject);
2 begin3 Application.OnMessage := AppMessage;
4 end;
5 6 procedure TMainform.Appmessage(var Msg: TMsg; var Handled: Boolean);
7 var8 message: TWMKey;
9 begin10 if (msg.message = WM_KEYDOWN) and (LoWord(msg.wparam) = VK_TAB) and11 (GetKeyState(VK_CONTROL) < 0) and Assigned(ActiveMDIChild) then12 begin13 Move(msg.message, message.msg, 3 * sizeof(Cardinal));
14 message.result := 0;
15 Handled := ActiveMDIChild.IsShortcut(message);
16 end;
17 end;
This will redirect Ctrl+Tab (and Ctrl+Shift+Tab) to the active MDI childs
IsShortcut function. This fires the OnShortcut event, so we can use that event on
the child form to further handle the key event:
18 19 function IsOnTabsheet(aControl: TWinControl; var pc: TPageControl): Boolean;
20 begin21 while Assigned(aControl) andnot (aControl is TTabsheet) do22 aControl := aControl.Parent;
23 Result := Assigned(aControl);
24 if result then25 pc := TTabSheet(aControl).Pagecontrol;
26 end;
27 28 procedure TMDIChild.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
29 var30 pc: TPageControl;
31 begin32 if (msg.CharCode = VK_TAB) and (GetKeyState(VK_CONTROL) < 0) then33 begin34 if IsOnTabsheet(ActiveControl, pc) then35 begin36 pc.Perform(CM_DIALOGKEY, msg.CharCode, 0);
37 Handled := true;
38 end;
39 end;
40 end;
That seems to do the trick.