文档切换与激活行为的优化

在许多专业编辑器中,例如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应用程序的人都可以将其转变为类似专业编辑器的行为。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485