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

CTreePropSheetEx – CTreePropSheet 的扩展版本

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (100投票s)

2004年8月4日

CPOL

13分钟阅读

viewsIcon

457243

downloadIcon

17736

CTreePropSheetEx 是 CTreePropSheet 的一个扩展,提供了诸如可调整大小、跳过空页以及新的属性框架(如 Office 2003 选项页)等新功能。

CTreePropSheetEx examples

CTreePropSheetOffice2003 用于 Office 2003 模式。

CTreePropSheetEx examples

在启用 XP 主题的情况下调整 CTreePropSheetEx 的大小。

CTreePropSheetEx examples

CTreePropSheetBordered,一个派生自 CTreePropSheetEx 的类,具有带边框的页面框架,未启用 XP 主题。

目录

引言

CTreePropSheetEx 是对 Sven Wiegand 编写的优秀 CTreePropSheet[^] 的改进。与原始类相比,它提供了以下附加功能:

  • 属性页在运行时可调整大小。
  • 导航树也可通过编程方式或用户手动调整大小。
  • 导航树支持以下调整大小策略:
    • 固定宽度,
    • 宽度与属性页成比例变化。
  • 新的页面框架
    • CPropPageFrameEx。它引入了无闪烁的渐变标题。它还提供了使用 GDI GradientFill 函数的选项。
    • CPropPageFrameBordered。基于 CPropPageFrameEx,此类会在页面周围绘制边框,即使对于不支持主题的应用程序也是如此(请参阅示例图片)。
  • 跳过空页。
  • 配置帮助上下文菜单。
  • 树形和标签模式下的高彩色图标。
  • 设置树形和框架的最小窗格大小。
  • Office 2003 选项页的外观和感觉。
  • 展开树中的所有项。
  • 启用/禁用属性页 - 0.4 版新增

这组类使用了 CodeProject 上发布的许多组件。有关所用组件的完整列表,请参阅致谢部分。

集成与配置

如何集成 CTreePropSheetEx

使用 CTreePropSheetEx 与使用 CPropertySheetCTreePropSheet 非常相似。此时,如果您还没有这样做,我建议您阅读 Sven 关于 CTreePropSheet [^] 的文章。在接下来的解释中,我将假设您已经创建了属性页及其页面。要使用该类,您需要:

  1. 将以下文件添加到您的项目中

文件名 (37 个文件)

描述

TreePropSheetEx.h
TreePropSheetEx.cpp
CTreePropSheetEx 类。
TreePropSheetBordered.h
TreePropSheetBordered.cpp
CTreePropSheetBordered 类,派生自 CTreePropSheetEx,在 XP 主题不可用时在框架周围绘制边框。
TreePropSheetOffice2003.h
TreePropSheetOffice2003.cpp
CTreePropSheetOffice2003 类,派生自 CTreePropSheetEx,提供类似于 Office 2003 选项对话框的外观和感觉。
TreePropSheetBase.h
TreePropSheetBase.cpp
CTreePropSheetBase 类,CTreePropSheetEx 的基类。
TreePropSheetTreeCtrl.h
TreePropSheetTreeCtrl.cpp
CTreePropSheetTreeCtrl 类,派生自 CTreeCtrl,允许为每个树项自定义颜色。基于 CTreeCtrlEx[^]。
PropPageFrame.h
PropPageFrame.cpp
CPropPageFrame 类。在树形模式下绘制在页面周围的框架的抽象基类。
PropPageFrameEx.h
PropPageFrameEx.cpp
CPropPageFrameEx 类。CPropPageFrame 的实现,提供无闪烁的框架。它是 CTreePropSheetBaseCTreePropSheetEx 默认使用的类。
PropPageFrameBordered.h
PropPageFrameBordered.cpp
CPropPageFrameBordered 类。派生自 CPropPageFrameEx,通过在 XP 主题不可用时在框架周围绘制边框来扩展它。
PropPageFrameOffice2003.h
PropPageFrameOffice2003.cpp
CPropPageFrameOffice2003 类。派生自 CPropPageFrameEx,通过提供类似于 Office 2003 选项对话框的外观和感觉来扩展它。
TreePropSheetSplitter.h
TreePropSheetSplitter.cpp
CTreePropSheetSplitter 类。基于 CSimpleSplitterWnd[^],实现了分隔器类。
TreePropSheetUtil.hpp 杂项工具类。
ThemeLibEx.h
ThemeLibEx.cpp
用于访问 XP 主题函数的帮助类。
TreePropSheetResizableLibHook.h
TreePropSheetResizableLibHook.cpp
通过消息挂钩而不是继承实现可调整大小的库。
ResisableGrip.h
ResizableGrip.cpp
ResizableLayout.h
ResizableLayout.cpp
ResizableMinMax.h
ResizableMinMax.cpp
ResizableMsgSupport.h
ResizableMsgSupport.inl
ResizableState.h
ResizableState.cpp

Resizable library[^] 类。

Hookwnd.h
Hookwnd.cpp
CHookWnd[^] 类。定义了 MFC 类实现消息挂钩的接口。
memDC.h CMemDC[^] 类。实现了允许无闪烁绘制的内存设备上下文。
HighColorTab.hpp 动态更新标签的图像列表以支持 1600 万色图标。
ResizablePage.h
ResizablePage.cpp

Resizable library for property pages[^] 类。CTreePropSheetEx 不直接使用,但可用于为属性页添加调整大小功能。

  1. 使用 CTreePropSheetExCTreePropSheetBorderedCTreePropSheetOffice2003 替代 CPropertySheet。如果您已经有一个派生自 CPropertySheet 的类,则需要派生自 CTreePropSheetEx(或 CTreePropSheetBorderedCTreePropSheetOffice2003)。您还需要将所有对 CPropertySheet 的引用替换为 CTreePropSheetEx(或 CTreePropSheetBorderedCTreePropSheetOffice2003)。如果您直接使用 CPropertySheet 对象,只需将其类型更改为 CTreePropSheetEx(或 CTreePropSheetBorderedCTreePropSheetOffice2003)。

    注意: CTreePropSheetExCTreePropSheetBorderedCTreePropSheetOffice2003 位于 TreePropSheet 命名空间中。

    注意: CHookWnd 使用 CCriticalSection。此类定义在 <afxmt.h> 中,因此您应在预编译头文件中添加以下行:

    #include <afxmt.h>

    另外,CPropPageFrame 使用 dynamic_cast,这意味着 RTTI 支持应已启用。

  2. 如果您选择使属性页可调整大小(这是默认启用的选项),您还必须为您的属性页添加调整大小支持。最简单的方法是使用 Paolo Messina 的 CResizablePage 类。以下代码片段演示了如何在示例应用程序的一个属性页中完成此操作:
    1. 将您的属性页的基类从 CPropertyPage 更改为 CResizablePage
    2. 编辑每个页面的 OnInitDialog 方法,以便为页面中的控件添加大小约束。例如,演示中的一个页面如下所示:
      BOOL CPageEmail::OnInitDialog()
      {
        CResizablePage::OnInitDialog();
      
        // Preset layout
        AddAnchor(IDC_EMAIL1, TOP_LEFT, TOP_RIGHT);
        AddAnchor(IDC_EMAIL2, TOP_LEFT, TOP_RIGHT);
        AddAnchor(IDC_EMAIL3, TOP_LEFT, TOP_RIGHT);
        AddAnchor(IDC_COMBO_DEFAULT_EMAIL, TOP_LEFT, TOP_RIGHT);
          
        return TRUE;
      }

      有关更多信息,您应该阅读 Paolo 的 文章[^]。

  3. 如果您使用 CTreePropSheetOffice2003,则需要自定义每个属性页以绘制白色背景(或者更准确地说,具有系统颜色 COLOR_WINDOW 的背景)。为此,您需要为每个属性页处理 WM_CTLCOLOR。此消息在每个子控件绘制之前发送给父页面,让父页面在控件渲染之前准备 DC。新的消息处理程序应如下所示:
    HBRUSH CPageDates::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
    {
      pDC->SetBkMode(TRANSPARENT);
      return ::GetSysColorBrush( COLOR_WINDOW );
    }

    此代码是最小的默认实现。您可以查看演示项目,了解如何更新属性页以使其能够在“标准模式”和 Office 2003 模式下进行渲染。

配置 (API)

API 的完整参考已使用 Doxygen[^] 生成。本文档顶部提供了链接。

命名空间

CTreePropSheetEx 相关的所有代码都放在 TreePropSheet 命名空间中。这与 CTreePropSheet 的命名空间相同。此框架使用的其他一些类位于它们自己的命名空间或全局命名空间中。

编译标志

在编译时设置两个功能:

  • 要使用所有者绘制渐变而不是 GDI 渐变,请取消注释 PropPageFramEx.cpp 顶部的以下行:
    #define USE_OWNER_DRAW_GRADIANT

    这将为您在旧操作系统(Win 95 和 Win NT 4.0)上提供更好的兼容性。

  • 要在 XP 和 2003 上启用 XP 主题,请在 ThemeLibEx.h 中取消注释以下行:
    #define XPSUPPORT

然后,当你开始迭代 2(这是构建迭代的开始)时,你可能想要复制测试用例并将它们重新分类到迭代 2。这还允许对测试用例进行粒度跟踪,并允许你说某个测试用例在一个迭代中是准备好的,但在另一个迭代中不是。同样,如何做到这一点取决于你以及你希望如何报告。 “场景”部分提供了更多细节。

CTreePropSheetBase();
CTreePropSheetBase(UINT nIDCaption,
                   CWnd* pParentWnd = NULL,
                   UINT iSelectPage = 0);
CTreePropSheetBase(LPCTSTR pszCaption,
                   CWnd* pParentWnd = NULL,
                   UINT iSelectPage = 0);

构造函数是 CPropertySheet 构造函数的重载。

初始化

BOOL SetTreeViewMode(BOOL bTreeViewMode = TRUE,
                     BOOL bPageCaption = FALSE,
                     BOOL bTreeImages = FALSE);

将属性页设置为树形模式,并指定是否显示页面标题。还指示树项是否应具有图标。

调整大小功能

bool SetIsResizable(const bool bIsResizable);
bool IsResizable() const;

启用/禁用属性页的调整大小功能。属性页默认是可调整大小的。您必须在创建窗口(调用 DoModalCreate)之前调用 SetIsResizable,否则调用将被忽略并返回 false

void SetMinSize(const CSize& sizeMin);
CSize GetMinSize() const;

设置/获取属性页的最小大小。

void SetMaxSize(const CSize& sizeMax);
CSize GetMaxSize() const;

设置/获取属性页的最大大小。

void SetPaneMinimumSize(const int nPaneMinimumSize);
bool SetPaneMinimumSizes(const int nTreeMinimumSize, const int nFrameMinimumSize);
int GetPaneMinimumSize() const;    // Deprecated. Use GetPaneMinimumSizes instead.
void GetPaneMinimumSizes(int& nTreeMinimumSize, int& nFrameMinimumSize) const;

设置/获取窗格(树形和框架)的最小大小。SetPaneMinimumSizeSetPaneMinimumSizes 必须在创建窗口(调用 DoModalCreate)之前调用,否则调用将被忽略并返回 false

SetPaneMinimumSize 将树形和框架的最小大小设置为指定值,而 SetPaneMinimumSizes 允许为树形和框架设置单独的最小大小值。

注意:自 0.2 版以来,推荐使用 GetPaneMinimumSizes 而不是 GetPaneMinimumSize,因为此方法不支持单独的最小大小,并且在这种情况下列出时返回 -1

树形调整大小功能

void SetTreeIsResizable(const bool bIsTreeResizable);
bool IsTreeResizable() const;

允许/禁止使用鼠标调整树形宽度。只要对话框可调整大小(IsResizable 返回 true),树形仍然可以通过编程方式调整大小。光标分隔器会根据树形的调整大小能力进行更新。

int GetSplitterWidth() const;
bool SetSplitterWidth(const int nSplitterWidth);

配置分隔器的宽度。您必须在创建窗口(调用 DoModalCreate)之前调用 SetSplitterWidth,否则调用将被忽略并返回 false

void SetTreeResizingMode(const enTreeSizingMode eSizingMode);
enTreeSizingMode GetTreeResizingMode() const;

描述了当属性页调整大小时树形应如何调整大小。选项有:

  • TSM_Fixed:当属性页调整大小时,树形的宽度不变。有一个例外,如果属性页已达到其最小大小(最小窗格大小),则树形会缩小。
  • TSM_Proportional:树形与属性页成比例地增长和缩小。
virtual BOOL SetTreeWidth(int nWidth);
int GetTreeWidth() const;

设置/获取树形宽度。请注意,即使在属性页窗口创建后,也可以更改树形宽度。但是,属性页必须是可调整大小的(IsResizable 返回 true)。

分隔器更新功能

void SetRealTimeSplitter(const bool bRealtime);
bool IsRealTimeSplitter() const;

定义在用户拖动分隔器时是否应实时调整窗格(树形和框架)的大小。

空页、空页文本

void SetSkipEmptyPages(const bool bSkipEmptyPages);
bool IsSkippingEmptyPages() const;

允许/禁止显示空页。

void SetEmptyPageText(LPCTSTR lpszEmptyPageText);
DWORD SetEmptyPageTextFormat(DWORD dwFormat);

设置要显示在空页中的文本内容和格式。

默认图标

BOOL SetTreeDefaultImages(CImageList *pImages);
BOOL SetTreeDefaultImages(UINT unBitmapID, int cx,COLORREF crMask);

设置空页和没有图标的页面的图标。

帮助上下文菜单配置功能

void SetContextMenuMode(const enContextMenuMode eContextMenuMode);
TreePropSheet::enContextMenuMode GetContextMenuMode() const;

定义如何处理帮助上下文菜单。Win32 属性页在用户右键单击属性页上的不同控件时会显示“这是什么?”上下文菜单,并为它“拥有”的控件(如“确定”和“取消”按钮)提供一些相关的帮助。对于添加到属性页的控件(如树形和框架),会显示一条通用消息,表明没有与该项关联的帮助主题。由于该消息无关紧要,因此对于这些控件不显示菜单是有意义的。如果您已为这些控件实现了帮助,请将值设置为 TPS_All,这样消息就不会被捕获。

选项有:

  • TPS_All:始终显示上下文菜单。
  • TPS_PropertySheetControls:仅为属性页控件显示上下文菜单,即不为树形和框架显示菜单。
  • TPS_None:从不显示上下文菜单。

树形自动展开

void SetAutoExpandTree(const bool bAutoExpandTree);
bool IsAutoExpandTree() const;

显示树形时自动展开树形中的所有项。您必须在创建窗口(调用 DoModalCreate)之前调用 SetAutoExpandTree,否则此调用将无效。

启用/禁用属性页 - 0.4 版新增

bool EnablePage(const CPropertyPage* const pPage, const bool bEnable);
bool IsPageEnabled(const CPropertyPage* const pPage) const;

第一个方法允许启用或禁用指定的属性页。第二个方法返回指定页面的启用状态。也可以通过向属性页发送以下消息来设置页面的启用状态:

Message ID: WMU_ENABLEPAGE (defined in TreePropSheetBase.h)
WPARAM: Pointer to property page
LPARAM: Enable/disable state

还编写了一个辅助方法来发送消息。hWndTarget 是属性页的 hWnd

inline BOOL Send_EnablePage(HWND hWndTarget, 
                 CPropertyPage* pPage, const bool bEnable)
{
  ASSERT(::IsWindow(hWndTarget));
  return (0 != ::SendMessage(hWndTarget, 
               WMU_ENABLEPAGE, (WPARAM)pPage, (LPARAM)bEnable));
}

该句柄内部调用 EnablePage

实现与扩展

以下是显示 CTreePropSheetEx 所使用的一些类的类图。

扩展属性页

类使用的所有内部对象都是通过工厂方法创建的。这允许您在需要时使用其他类。方法是:

virtual CTreeCtrl* CreatePageTreeObject();
virtual CPropPageFrame* CreatePageFrame();
virtual tSplitterPtr CreateSplitterObject() const;
virtual tLayoutManagerPtr CreateLayoutManagerObject();

以下方法允许您检索类使用的内部对象:

CTreeCtrl* GetPageTreeControl();
CPropPageFrame* GetFrameControl();
virtual tSplitter* GetSplitterObject() const;
virtual CWnd* GetSplitterWnd() const;
virtual tLayoutManager GetLayoutManagerObject() const;

属性框架

如上所述,CPropPageFrameEx 实现双缓冲以减少闪烁。此外,该类有一个新的虚方法 DrawBackground,顾名思义,它在需要重绘框架背景时被调用。CPropPageFrameBordered 重载此方法以在不支持 XP 主题的平台上显示边框。方法签名是:

virtual void DrawBackground(CDC* pDC);

此外,CPropPageFrameEx 现在拥有并公开 XP 主题帮助类,以便派生类可以使用它。访问帮助类的方法是:

const CThemeLibEx& GetThemeLib() const;

使用带边框的框架

带边框的框架(位于 PropPageFrameBordered.hPropPageFrameBordered.cpp 中的 CPropPageFrameBordered)默认不使用。它允许在应用程序不使用 XP 主题时在活动属性页周围绘制边框。如果要使用此功能,则需要创建一个继承自 CTreePropSheetEx(或 CTreePropSheet)的新类,在实现文件中包含 PropPageFrameBordered.h,并重载 CreatePageFrame 方法,如下所示:

CPropPageFrame* CYourNewClassName::CreatePageFrame()
{
  return new CPropPageFrameBordered;
}

实现细节和要点

创建 CTreePropSheetBase 而不是重用现有的 CTreePropSheet

创建 CTreePropSheetBase 而不是派生自 CTreePropSheet 的主要原因是需要更新该类(主要是更改某些方法和属性的访问权限)。我还对代码进行了一些修复。拥有新类还意味着您可以在不更改使用 CTreePropSheet 的任何代码的情况下开始使用 CTreePropSheetEx

由于 CThemeLib 位于 CPropPageFrameDefault.cpp 文件中,因此无法重用此代码,这就是我必须将其从该文件中提取出来的原因。CThemeLibEx 的功能与原始类相同。我还更改了方法的 const 性,以便能够返回对该类的 const 引用。

布局管理器

布局管理器通过子类化而不是继承集成到属性页中。这样,可以在运行时将调整大小设为一项选项。当选择不使用任何调整大小功能时,不会创建布局管理器或分隔器对象。

跳过空白页

实现此功能很简单,除了键盘处理。所有处理都在 CTreePropSheetBase::PreTranslateMessage 中执行,我们可以在按键被路由到树形控件之前拦截它们。

启用和禁用属性页

启用或禁用属性页必须通过属性页 API(调用 EnablePage)或发送 WMU_ENABLEPAGE 消息来完成。另一种解决方案是,在将每个属性页添加到属性页时为其添加一个挂钩,并处理 WM_ENABLE 消息以执行适当的操作。虽然我认为这是可行的,但我没有这样做,因为我不认为很多应用程序会启用和禁用属性页(我可能错了),主要是因为属性页控件不会向用户提供已禁用页面的任何视觉反馈。

该功能的实现需要使用树形控件的扩展版本。此版本允许通过调用 SetItemColor 或发送 WMU_ENABLETREEITEM 消息来设置树项的颜色。使用消息机制意味着该控件仍然由属性页(在 CTreePropSheetBase 中)存储为 CTreeCtrl。如果您已经有一个自定义树形控件,则需要在实现中处理 WM_PAINTWMU_ENABLETREEITEM

计划的增强功能

  • 欢迎提出任何建议。请告诉我,以便它们可以包含在将来的版本中。
  • 添加一个选项以使用 GDI 渐变方法,并在所需库在系统上不可用时回退到所有者绘制。

要求

系统

我已在 Windows 2000/XP 和 2003 Server 上测试了示例程序。我将尝试在有权访问这些平台时在 Windows 95、98 和 NT 4.0 上进行测试。

编译器

我在 Visual C++ 6.0、Visual Studio 2003 和 Visual Studio 2005 Beta 上编译了代码。

Win 98 或更高版本以及 Windows 2000 或更高版本支持 GDI 渐变。

如果要启用 XP 主题,还需要平台 SDK 才能编译。

致谢

CTreePropSheetEx 使用了许多在 CodeProject 上发布的组件。非常感谢:

感谢以下人员报告问题并提出修复建议:

希望我没有遗漏任何人。

版本历史

  • 2004年8月3日:0.1:初始发布。
  • 2004年8月14日:0.2:新增功能
    • 添加了 CTreePropSheetBordered 类。
    • 树形和框架的最小尺寸分开。
  • 2004年9月13日:0.3:新增功能
    • 添加了 CTreePropSheetOffice2003 类。
    • CTreePropSheetBase 中添加了自动展开树形功能。
  • 2004年9月14日:修复了图像大小和文章的少量更新。
  • 04/03/2005:
    • 实现了迄今为止报告的所有建议的 bug 修复,
    • 添加了属性页的启用/禁用功能。

许可证

本文中的文件根据 Artistic License 条款发布。您可以在此处获取该许可证的副本。

Paolo Messina 很好地总结了该许可证,他说该许可证“允许在商业应用程序中使用。您只是不能将此作品作为库的一部分进行销售并声称是您自己的。这也意味着不需要注明出处,但注明出处会更好!”

© . All rights reserved.