65.9K
CodeProject 正在变化。 阅读更多。
Home

CMDITabs

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (43投票s)

2001年10月7日

CPOL

4分钟阅读

viewsIcon

392576

downloadIcon

12306

一个 CTabCtrl 控件, 可方便地在 MDI 视图之间切换

概述

在大多数 MDI 产品(如 Microsoft Word 或 Microsoft FrontPage)中,我一直缺少一种简单的方法来查看当前打开了哪些视图/文档,并方便地在它们之间切换。使用“窗口”菜单可能会非常麻烦。我想要一些类似 Oz Solomonovich 的 Window Tabs Add-In 的功能。在我的当前项目中,我必须做一个包含多个不同视图的 MDI 应用程序,我不希望我的用户在这方面和我一样感到沮丧。所以我决定自己创建一个 CTabCtrl 的派生类。事实证明,这非常容易做到。

CMDITabs 是一个小型控件,为 MFC MDI 应用程序添加了“标签”,用户可以通过这些标签在视图之间切换。其外观也可以自定义,以显示视图图标。如果您有不同类型的视图,这将非常有用,否则我建议关闭图标。标签可以放置在视图的底部或顶部。该控件足够智能,可以反映视图中的所有更改(打开、关闭、新建、更改标题/图标)。

在撰写本文后,我发现 Code Project 的 文档/视图 部分有一个用于 MDI 视图之间标签页的类似解决方案。起初我有点沮丧,以为我又在重复造轮子了 ;-)。但仔细一看,我更喜欢我的 MDITabs,原因有三:

  1. 它看起来更好,并且没有浪费空间的双边框框架
  2. 由于其更简单的实现,它更稳定 ;-)(请阅读评论中的错误列表)
  3. 它更小,您只需要使用一个类,而不是三个

所以我将这篇文章提交给了 Code Project。

如何使用

在代码中使用 CMDITabs 非常简单。将 MDITabs.hMDITabs.cpp 添加到您的项目中。在您的 CMainFrame 类中,添加一个 CMDITabs 类型的成员 m_wndMDITabs(不要忘记包含 'MDITabs.h')。在 CMainFrame::OnCreate() 中,在所有工具栏和状态栏设置创建完成后,插入 m_mdiTabs.Create(this);。为了获得正确的布局,MDI 标签控件必须最后创建。为了同步视图操作与 MDI 标签,有必要覆盖我们 CMainFrame 类基类 CMDIFrameWndOnUpdateFrameTitle。调用基类实现后,您必须调用 m_wndMDITabsUpdate() 函数。

// 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 复杂的文档/视图组织!解决方案越简单,工作就越健壮!

© . All rights reserved.