CMDITabs






4.88/5 (43投票s)
一个 CTabCtrl 控件,
概述
在大多数 MDI 产品(如 Microsoft Word 或 Microsoft FrontPage)中,我一直缺少一种简单的方法来查看当前打开了哪些视图/文档,并方便地在它们之间切换。使用“窗口”菜单可能会非常麻烦。我想要一些类似 Oz Solomonovich 的 Window Tabs Add-In 的功能。在我的当前项目中,我必须做一个包含多个不同视图的 MDI 应用程序,我不希望我的用户在这方面和我一样感到沮丧。所以我决定自己创建一个 CTabCtrl
的派生类。事实证明,这非常容易做到。
CMDITabs
是一个小型控件,为 MFC MDI 应用程序添加了“标签”,用户可以通过这些标签在视图之间切换。其外观也可以自定义,以显示视图图标。如果您有不同类型的视图,这将非常有用,否则我建议关闭图标。标签可以放置在视图的底部或顶部。该控件足够智能,可以反映视图中的所有更改(打开、关闭、新建、更改标题/图标)。
在撰写本文后,我发现 Code Project 的 文档/视图 部分有一个用于 MDI 视图之间标签页的类似解决方案。起初我有点沮丧,以为我又在重复造轮子了 ;-)。但仔细一看,我更喜欢我的 MDITabs,原因有三:
- 它看起来更好,并且没有浪费空间的双边框框架
- 由于其更简单的实现,它更稳定 ;-)(请阅读评论中的错误列表)
- 它更小,您只需要使用一个类,而不是三个
所以我将这篇文章提交给了 Code Project。
如何使用
在代码中使用 CMDITabs
非常简单。将 MDITabs.h 和 MDITabs.cpp 添加到您的项目中。在您的 CMainFrame
类中,添加一个 CMDITabs
类型的成员 m_wndMDITabs
(不要忘记包含 'MDITabs.h')。在 CMainFrame::OnCreate()
中,在所有工具栏和状态栏设置创建完成后,插入 m_mdiTabs.Create(this);
。为了获得正确的布局,MDI 标签控件必须最后创建。为了同步视图操作与 MDI 标签,有必要覆盖我们 CMainFrame
类基类 CMDIFrameWnd
的 OnUpdateFrameTitle
。调用基类实现后,您必须调用 m_wndMDITabs
的 Update()
函数。
// MainFrm.h class CMainFrame : public CMDIFrameWnd { [...] CMDITabs m_wndMDITabs; virtual void OnUpdateFrameTitle(BOOL bAddToTitle); [...] }; // MainFrm.cpp void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle) { CMDIFrameWnd::OnUpdateFrameTitle(bAddToTitle); m_wndMDITabs.Update(); // sync the mditabctrl with all views }
就是这样!构建并启动您的应用程序,打开一些视图,享受在它们之间切换的乐趣 ;-)
其他功能
- 双击标签可最大化视图。
- 右键单击标签可显示视图的系统菜单。如果您愿意,可以通过简单地更改视图的系统菜单来更改菜单(请参阅演示项目
CChildFrame::OnCreate()
)。如果您需要提供一个完全不同的(独立的)菜单,您的视图可以响应WM_GETTABSYSMENU
消息。 - 当打开的视图少于一个(两个)时,标签将隐藏。使用
SetMinViews()
或样式MT_HIDEWLT2VIEWS
来更改此行为。 - 在
Create()
函数中,您可以提供一些样式来定制控件的外观MT_BOTTOM 标签出现在底部 MT_TOP 标签出现在顶部 MT_IMAGES 使用视图图标作为图像 MT_HIDEWLT2VIEWS 当视图少于两个时隐藏标签(默认是一个视图)
m_wndMDITabs.Create(this, MT_TOP|MT_IMAGES|MT_HIDEWLT2VIEWS);
内部
布局
私有 MFC 消息 WM_SIZEPARENT
提供了一种将窗口附加到文档/视图应用程序的 MDI 客户端区域的方法。CMDITabs
实现 OnSizeParent
将其附加到 MDI 客户端区域的底部。如果您想要其他布局,则需要更改此函数。MDIClient 同辈的 Z 顺序对于布局算法很重要。同辈被要求以 Z 顺序定位自己(请搜索 MSDN 上的 WM_SIZEPARENT
以获取更多信息)。这就是为什么 CMDITabs
必须在 CMainFrame::OnCreate()
中创建所有其他控件和状态栏之后创建的原因。否则,状态栏将出现在标签之上,破坏正确的布局。
同步视图和标签
标签控件必须始终反映视图列表。我没有监视所有可能的视图更改事件(关闭、打开、新建、更改标题/图标),而是挂钩到 CMainFrame::OnUpdateFrameTitle()
函数。我发现,当发生与视图相关的事件时,会调用此函数。您必须在此处调用 CMDITabs::Update()
,它会完全重建其标签列表。它通过查询 MDIClient 窗口的子窗口来做到这一点,从而绕过了 MFC 复杂的文档/视图组织!解决方案越简单,工作就越健壮!