在许多专业编辑器中,例如Visual Studio,可以通过Ctrl+Tab快捷键在打开的文档之间切换。每次按下Tab键时,如果Ctrl键被按住,就会切换到下一个打开的文档。如果需要返回到之前的文档,可以通过继续按下Tab键来实现。这种行为在专业编辑器中很常见,例如Visual Studio、Notepad++,甚至是一些浏览器,如Opera。本文将介绍如何在默认的MDI应用程序中实现类似的文档切换和激活行为。
在默认的MDI应用程序中,通常会遇到两种情况:
情况1:按下Ctrl+Tab切换到下一个文档,保持Ctrl键按下,再次按下Tab键切换到下一个文档,以此类推。
情况2:按下Ctrl+Tab切换到下一个文档,释放Ctrl键,再次按下Ctrl+Tab切换到下一个文档,释放Ctrl键,再次按下Ctrl+Tab切换到下一个文档。
这两种情况揭示了一个事实:即使使用不同的按键组合,也会在默认的MDI应用程序中获得相同的行为。然而,在Visual Studio编辑器中,情况2中的行为会有所不同:它会激活上一个文档,而不是下一个文档。这更加方便,尤其是当打开了很多文档时。
为了在默认的MDI应用程序中实现类似于Visual Studio编辑器的文档切换和激活行为,需要进行一些简单的修改。以下是主要的步骤:
在CMainFrame中添加一个打开的子框架的指针数组:
CTypedPtrArray m_arrChild;
然后处理添加、移除和同步打开的子框架与这个指针数组的关系。
在CMainFrame中处理子框架添加、移除、激活和被激活的消息:
ON_MESSAGE(WMU_CHILDFRAMEADDED, &CMainFrame::OnChildFrameAdded)
ON_MESSAGE(WMU_CHILDFRAMEREMOVED, &CMainFrame::OnChildFrameRemoved)
ON_MESSAGE(WMU_CHILDFRAMEACTIVATE, &CMainFrame::OnChildFrameActivate)
ON_MESSAGE(WMU_CHILDFRAMEACTIVATED, &CMainFrame::OnChildFrameActivated)
实现消息处理函数,例如:
LRESULT CMainFrame::OnChildFrameAdded(WPARAM wParam, LPARAM lParam) {
for (int i = 0; i < m_arrChild.GetSize(); ++i) {
if (reinterpret_cast(lParam) == m_arrChild.GetAt(i)) {
return 0;
}
}
m_arrChild.InsertAt(0, (CMDIChildWnd*)lParam);
return 1;
}
在子框架中发送关于创建、销毁和激活子框架的信息:
CChildFrame::CChildFrame() noexcept {
::PostMessage(AfxGetMainWnd()->GetSafeHwnd(), WMU_CHILDFRAMEADDED, 0, reinterpret_cast(this));
}
CChildFrame::~CChildFrame() {
::PostMessage(AfxGetMainWnd()->GetSafeHwnd(), WMU_CHILDFRAMEREMOVED, 0, reinterpret_cast(this));
}
void CChildFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd) {
CMDIChildWndEx::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
if (bActivate) {
::PostMessage(AfxGetMainWnd()->GetSafeHwnd(), WMU_CHILDFRAMEACTIVATED, 0, reinterpret_cast(this));
}
}
添加CWindowsManagerDialog类到项目中,以便在按下Ctrl+Tab时列出打开的子框架。
在CMainFrame和CWindowsManagerDialog中处理PreTranslateMessage以跟踪用户的按键操作:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) {
if (WM_KEYDOWN == pMsg->message && VK_TAB == pMsg->wParam && GetAsyncKeyState(VK_CONTROL) < 0 && m_arrChild.GetSize() > 1) {
CWindowsManagerDialog* pDlg = new CWindowsManagerDialog;
pDlg->Create(CWindowsManagerDialog::IDD, this);
pDlg->ShowWindow(SW_SHOW);
return TRUE;
}
return CMDIFrameWndEx::PreTranslateMessage(pMsg);
}
在CMainFrame中使用了IsTabbedMDIChild()调用,这可能在旧版本的Visual Studio中不可用。如果是这种情况,应该在CMainFrame头文件中这样写:
BOOL IsTabbedMDIChild() const {
return NULL != GetSafeHwnd() && AreMDITabs();
}
如果认为这很有用,可以根据自己的需要进行调整、修改和实现。
通过上述简单的修改,任何拥有MDI应用程序的人都可以将其转变为类似专业编辑器的行为。