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

创建IE风格GUI的一组类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (49投票s)

2001 年 5 月 3 日

CPOL

6分钟阅读

viewsIcon

597594

downloadIcon

6376

本文介绍了一个实现 Internet Explorer 风格的可调整大小的 re-bar 和菜单栏控件的示例。

引言

我最近一直在寻找一个模仿 IE 菜单栏的示例。首先,我阅读了 MSDN 库中一篇题为“创建 Internet Explorer 风格的菜单栏”的文章,它简要介绍了如何开始。然后,我偶然发现了 Paul DiLascia 的一篇文章(附带一个可运行的示例!)。不幸的是,Paul 的文章针对的是 SDI 应用程序而不是 MDI,尽管它提供了许多有用的想法和技巧,我后来都用上了。最后,我访问了专注于 MFC 编程的网站,并在那里找到了一些关于所讨论问题的优秀文章。但其中大多数文章似乎都想显得很通用,结果却过于臃肿。这可能是因为作者试图模仿 MS Office/DevStudio(这确实更复杂),而不是 IE 菜单栏。因此,我决定创建自己的 MenuBar,它将满足我所有不那么苛刻的需求。

随着我的 MenuBar 逐渐成型,我意识到像 IE 那样将标准的 ReBar 控件集成到另一个可调整大小的“re-bar”中也会很棒,这样用户就可以通过拖动框架客户端区域和 ReBar 之间的分隔符来调整 ReBar 的大小。所以这就催生了另一个控件,我在其中称之为 SizableReBar。实现相应的 C++ 类并不难,因为网上有很多自定义 ControlBars 的示例。我最喜欢的是 Cristi Posea 的 CSizingControlBar(感谢 Cristi 的出色工作!)。我在我的 CSizableReBar 类中也使用了一些他的基本思想。

毕竟,要完全模仿 IE 风格的 GUI,还有最后一件事需要实现,那就是一个工具栏控件,当它的某些按钮被裁剪时,能够显示在剪刀(chevron)菜单上。坦白说,我一开始对这个想法并不热衷,类库的初始版本也没有包含任何工具栏类。幸运的是,我前段时间有一些空闲时间,写了那个预期的 CToolBarEx 类。

虽然位图菜单并不是 IE 风格 GUI 的特色,但如今在现代应用程序中拥有它们被认为是良好的做法。网上有很多关于如何实现所有者绘制位图菜单的优秀文章。但我没有找到一篇利用 Windows 98 中引入的新函数和结构的文章,即 MENUITEMINFO 结构的 hbmpItem 成员以及 GetMenuInfo() / SetMenuInfo() 函数以及 MENUINFO 结构。在我看来,考虑到已经从 W95/NT4 迁移到 W98/W2K/XP 平台的用户的数量不断增长,使用这些函数/结构是相当合理的。而且,与使用完全所有者绘制的菜单相比,它也更容易,也是更标准的方式。这就是为什么我通过使用这些新函数/结构内置了对位图菜单的支持。

类的亮点

接下来是目前已实现的内容(所有这些类都位于演示项目中的 \GuiLib 目录,并编译成一个名为 GuiLib.dll 的 MFC 扩展 DLL)

  1. CMenuBar 类封装了菜单栏的核心功能。它还知道如何在某些菜单栏项(以及/或 MDI 应用程序中的标题按钮)被裁剪时,在剪刀菜单上显示自己。



    CMenuBar 的最新版本在嵌入项在位激活时似乎能正常工作。

  2. CToolBarEx 类用于替代标准的 CToolBar 类。与 CToolBar 不同,CToolBarEx 在某些按钮被裁剪时知道如何在剪刀菜单上显示自己。



    CToolBarEx 提供了 3 种不同的文本标签选项(“显示文本标签”、“选择性右侧文本”和“无文本标签”)和 2 种图标选项(“小图标”,即 16x16,和“大图标”,即 24x24)。

    此外,CToolBarEx 是持久的,即它的状态(包括文本和图标选项)可以保存在注册表中,并在以后恢复。相应的方法是:SaveState(LPCTSTR lpszProfileName)LoadState(LPCTSTR lpszProfileName)

  3. CCustomizeDialog 类允许用户更改工具栏选项以及进行其他工具栏自定义。实际上,它是标准的“自定义工具栏”对话框,只是略有扩展(请参见对话框底部的新的组合框)。



  4. CSizableReBar 类封装了可调整大小的 re-bar 控件的功能。

    该控件是持久的,即它的状态现在可以保存在注册表中,并在以后恢复。相应的方法是:SaveState(LPCTSTR lpszProfileName)LoadState(LPCTSTR lpszProfileName)

    CSizableReBar 有一个上下文菜单,通常分为两部分:第一部分是可以隐藏/显示的条带列表,第二部分(可选)特定于被单击的条带。例如,当用户右键单击带有工具栏的条带时,上下文菜单可能如下所示



  5. 引入了 CFrameWndEx(用于 SDI 应用程序)、CMDIFrameWndExCMDIChildWndEx(用于 MDI 应用程序)类,以使程序员的生活尽可能轻松。C(MDI)FrameWndEx 将自动创建 SizableReBar 和 MenuBar,并处理一些关键的窗口消息,以使 MenuBar 正确工作。

  6. 引入了 CPreviewToolBarCPreviewViewExtemplate<class TBase> class CPreviewableView 类,以提供打印预览模式,用外观漂亮的 CToolBarEx 派生工具栏替换 MFC 默认提供的那个丑陋的标准预览栏。



    要拥有这样的预览工具栏,您的视图应派生自 CPreviewableView。例如

    class CMyView : public CPreviewableView< CEditView >
    还应将 CPreviewableView 中实现的 ID_FILE_PRINT_PREVIEW 命令处理程序添加到派生类的消息映射中。

    BEGIN_MESSAGE_MAP( CMyView, CEditView )
      ...
      ON_COMMAND( ID_FILE_PRINT_PREVIEW, OnFilePrintPreview )
    END_MESSAGE_MAP()
    
  7. 每个被假定用作位图弹出菜单所有者的窗口都应派生自 template<class TBase> class CBmpMenuOwnerWnd。例如
    class CMyFrame : public CBmpMenuOwnerWnd< CMiniFrameWnd  >

    还应将 CBmpMenuOwnerWnd 中实现的 消息处理程序添加到派生类的消息映射中。

    BEGIN_MESSAGE_MAP( CMyFrame, CMiniFrameWnd )
      ...
      ON_WM_INITMENUPOPUP()
      ON_WM_DRAWITEM()
      ON_WM_MEASUREITEM()
    END_MESSAGE_MAP()
    注意:CFrameWndExCMDIFrameWndExCMDIChildWndEx 已派生自 CBmpMenuOwnerWnd

  8. CWindowListDialog 类允许用户管理 MDI 子窗口。



    显然,此对话框仅适用于 MDI 应用程序。如果存在窗口弹出菜单,则调用此对话框的菜单项会自动添加到其末尾。

  9. CWinAppEx 类用于缓存各种系统范围的设置。它还保存带有菜单图标的图像列表。

关于 CMenuBarCToolBarEx 类最新版本的通用说明:所有新的 W98/W2K/XP 视觉效果(如 XP 主题、平坦菜单外观、菜单动画、菜单下划线、菜单淡入等)现在都由菜单栏/工具栏剪刀菜单支持,并与相应的系统范围设置保持一致。

使用这些类

以下是基本步骤

  1. 将 GuiLib 目录添加到项目的其他包含目录列表中。
  2. 在您的 stdafx.h 中添加以下代码行:
    #include "GuiLib.h"
  3. 在您的 RC 文件中添加 GuiLib\Resource.h 的包含。
    #include "afxres.h"
    #include "GuiLib\Resource.h"
    
    ...
    
    2 TEXTINCLUDE MOVEABLE PURE
    BEGIN
      "#include ""afxres.h""\r\n"
      "#include ""GuiLib\\Resource.h""\r\n"
      ...
      "\0"
    END
    注意:资源 ID 从 101 到 999 以及从 33001 到 33999 已被 GuiLib.dll 保留,您的应用程序不应使用它们。
  4. 让您的 Windows 应用程序对象派生自 CWinAppEx 而不是 CWinApp,并按如下方式修改 InitInstance()
    BOOL YourApp::InitInstance()
    {
      InitGuiLibDLL();	// insert GuiLib.dll into the resource chain
    	
      ...
    }
  5. 让您的主框架对象派生自 C(MDI)FrameWndEx 而不是 C(MDI)FrameWnd。对于 MDI 应用程序,所有子框架都应派生自 CMDIChildWndEx
  6. 让您的所有工具栏派生自 CToolBarEx(不要忘记覆盖 CToolBarEx::Init() 函数!)。一旦工具栏创建完毕,就应该将其添加到可调整大小的 re-bar 控件中。这可以在 CMainFrame::OnCreate() 中完成。例如
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
      if ( CMDIFrameWndEx::OnCreate( lpCreateStruct ) == -1 )
        return -1;
    		
      VERIFY( m_wndToolBar.Create( this,
          WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP | CBRS_FLYBY ) );
      VERIFY( m_wndReBar.AddBar( &m_wndToolBar,
          0, 0, RBBS_FIXEDBMP | RBBS_BREAK, _T("&Toolbar"), false ) );
    
      m_wndToolBar.Init();
    	
      ...
    }

由于没有时间逐一演示每个类的用法,我可以这样说,对于一个中级 MFC 开发者来说,从演示项目的源代码中获取所有此处省略的细节和方面应该不是问题。

最低系统要求

如您可能知道,总会有美中不足之处:所有这些东西现在都需要 Comctl32.dll 版本 5.81 或更高版本。我个人认为 v5.80 和 v5.81 之间没有区别。根据我的经验,所有这些 v5.81 特定标志(如 BTNS_SHOWTEXT)与 v5.80 一起使用效果也很好。真是个谜!如果有人知道实际区别,请告诉我。

最后的寄语

不幸的是,我没有时间彻底测试这些类。因此,任何错误报告、错误修复和评论都非常欢迎!无论如何,我希望您会喜欢使用这一套类。

© . All rights reserved.