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 2 procedure TMainForm.FormCreate(Sender: TObject);
3 begin4 Application.OnMessage := AppMessage;
5 end;
6 7 procedure TMainform.Appmessage(var Msg: TMsg; var Handled: Boolean);
8 var9 message: TWMKey;
10 begin11 if (msg.message = WM_KEYDOWN) and (LoWord(msg.wparam) = VK_TAB) and12 (GetKeyState(VK_CONTROL) < 0) and Assigned(ActiveMDIChild) then13 begin14 Move(msg.message, message.msg, 3 * sizeof(Cardinal));
15 message.result := 0;
16 Handled := ActiveMDIChild.IsShortcut(message);
17 end;
18 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:
19 20 function IsOnTabsheet(aControl: TWinControl; var pc: TPageControl): Boolean;
21 begin22 while Assigned(aControl) andnot (aControl is TTabsheet) do23 aControl := aControl.Parent;
24 Result := Assigned(aControl);
25 if result then26 pc := TTabSheet(aControl).Pagecontrol;
27 end;
28 29 procedure TMDIChild.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
30 var31 pc: TPageControl;
32 begin33 if (msg.CharCode = VK_TAB) and (GetKeyState(VK_CONTROL) < 0) then34 begin35 if IsOnTabsheet(ActiveControl, pc) then36 begin37 pc.Perform(CM_DIALOGKEY, msg.CharCode, 0);
38 Handled := true;
39 end;
40 end;
41 end;
That seems to do the trick.