我在MDI 子窗體有個TabControl控件(如PageControl), 當按下Ctrl+Tab 或 Ctrl+Shift+Tab 組合鍵時,應用程序切換到了下一個MDI子窗口,而不是改變TabControl控件的活動頁,我怎樣才能強制MDI子窗體傳遞 Ctrl+Tab 組合鍵到 TabControl 控件那?
答案:
這實際上是API層面的衝突,在MDI程序中,消息處理每次都會調用 IsMDImsg 函數,並且這個函數會調用 TranslateMDISysAccel API 函數,Ctr+Tab 在這裏就被處理了,所以子窗口永遠不會收到這個鍵值的消息。
爲了能夠在子窗口中處理這個消息,我們需要在 IsMDIMsg 函數被調用前干預這個消息, 只要一個機會可以做這件事情,那就是Application.OnMessage 事件,所以要在主窗口的OnCreate 事件裏添加一個自定義的處理,暫時叫這個處理方法爲AppMessage;(注:實際上我們不用這麼麻煩,在Addtional 組件頁有個 ApplicationEvents 控件,我們可以把它直接拿下來用,它有一個OnMessage 事件,直接處理這個事件就行了), 一下是他的處理方法的代碼
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;
注:如果用我說的控件,直接將Appmessage 過程的代碼寫到 Application.OnMessage 事件裏就行了。
這樣就可以將 Ctrl+Tab (和 Ctrl+Shift+Tab) 重定向到當前活動的子窗口的IsShortCut 函數中,並激發OnShortcut 事件,所以我們就可以在子窗口的OnShortCut 事件裏處理Ctrl+Tab 這個鍵以後的事件了。
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;
原文:How to pass a [CTRL] [TAB] to a TPageControl on a MDI child form
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.