2011. január 9., vasárnap

How to pass a [CTRL] [TAB] to a TPageControl on a MDI child form


Problem/Question/Abstract:

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:

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.OnMessage := AppMessage;
end;

procedure TMainform.Appmessage(var Msg: TMsg; var Handled: Boolean);
var
  message: TWMKey;
begin
  if (msg.message = WM_KEYDOWN) and (LoWord(msg.wparam) = VK_TAB) and
    (GetKeyState(VK_CONTROL) < 0) and Assigned(ActiveMDIChild) then
  begin
    Move(msg.message, message.msg, 3 * sizeof(Cardinal));
    message.result := 0;
    Handled := ActiveMDIChild.IsShortcut(message);
  end;
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:

function IsOnTabsheet(aControl: TWinControl; var pc: TPageControl): Boolean;
begin
  while Assigned(aControl) and not (aControl is TTabsheet) do
    aControl := aControl.Parent;
  Result := Assigned(aControl);
  if result then
    pc := TTabSheet(aControl).Pagecontrol;
end;

procedure TMDIChild.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
var
  pc: TPageControl;
begin
  if (msg.CharCode = VK_TAB) and (GetKeyState(VK_CONTROL) < 0) then
  begin
    if IsOnTabsheet(ActiveControl, pc) then
    begin
      pc.Perform(CM_DIALOGKEY, msg.CharCode, 0);
      Handled := true;
    end;
  end;
end;

That seems to do the trick.

Nincsenek megjegyzések:

Megjegyzés küldése