CTreePropSheetEx – CTreePropSheet 的扩展版本






4.98/5 (100投票s)
CTreePropSheetEx 是 CTreePropSheet 的一个扩展,提供了诸如可调整大小、跳过空页以及新的属性框架(如 Office 2003 选项页)等新功能。
CTreePropSheetOffice2003
用于 Office 2003 模式。
在启用 XP 主题的情况下调整 CTreePropSheetEx
的大小。
CTreePropSheetBordered
,一个派生自 CTreePropSheetEx
的类,具有带边框的页面框架,未启用 XP 主题。
目录
引言
CTreePropSheetEx
是对 Sven Wiegand 编写的优秀 CTreePropSheet[^] 的改进。与原始类相比,它提供了以下附加功能:
- 属性页在运行时可调整大小。
- 导航树也可通过编程方式或用户手动调整大小。
- 导航树支持以下调整大小策略:
- 固定宽度,
- 宽度与属性页成比例变化。
- 新的页面框架
CPropPageFrameEx
。它引入了无闪烁的渐变标题。它还提供了使用 GDIGradientFill
函数的选项。CPropPageFrameBordered
。基于CPropPageFrameEx
,此类会在页面周围绘制边框,即使对于不支持主题的应用程序也是如此(请参阅示例图片)。
- 跳过空页。
- 配置帮助上下文菜单。
- 树形和标签模式下的高彩色图标。
- 设置树形和框架的最小窗格大小。
- Office 2003 选项页的外观和感觉。
- 展开树中的所有项。
- 启用/禁用属性页 - 0.4 版新增。
这组类使用了 CodeProject 上发布的许多组件。有关所用组件的完整列表,请参阅致谢部分。
集成与配置
如何集成 CTreePropSheetEx
使用 CTreePropSheetEx
与使用 CPropertySheet
或 CTreePropSheet
非常相似。此时,如果您还没有这样做,我建议您阅读 Sven 关于 CTreePropSheet [^] 的文章。在接下来的解释中,我将假设您已经创建了属性页及其页面。要使用该类,您需要:
- 将以下文件添加到您的项目中
文件名 (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 的实现,提供无闪烁的框架。它是 CTreePropSheetBase 和 CTreePropSheetEx 默认使用的类。 |
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
、CTreePropSheetBordered
或CTreePropSheetOffice2003
替代CPropertySheet
。如果您已经有一个派生自CPropertySheet
的类,则需要派生自CTreePropSheetEx
(或CTreePropSheetBordered
或CTreePropSheetOffice2003
)。您还需要将所有对CPropertySheet
的引用替换为CTreePropSheetEx
(或CTreePropSheetBordered
或CTreePropSheetOffice2003
)。如果您直接使用CPropertySheet
对象,只需将其类型更改为CTreePropSheetEx
(或CTreePropSheetBordered
或CTreePropSheetOffice2003
)。注意:
CTreePropSheetEx
、CTreePropSheetBordered
和CTreePropSheetOffice2003
位于TreePropSheet
命名空间中。注意:
CHookWnd
使用CCriticalSection
。此类定义在 <afxmt.h> 中,因此您应在预编译头文件中添加以下行:#include <afxmt.h>
另外,
CPropPageFrame
使用dynamic_cast
,这意味着 RTTI 支持应已启用。 - 如果您选择使属性页可调整大小(这是默认启用的选项),您还必须为您的属性页添加调整大小支持。最简单的方法是使用 Paolo Messina 的
CResizablePage
类。以下代码片段演示了如何在示例应用程序的一个属性页中完成此操作:- 将您的属性页的基类从
CPropertyPage
更改为CResizablePage
。 - 编辑每个页面的
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; }
- 将您的属性页的基类从
- 如果您使用
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;
启用/禁用属性页的调整大小功能。属性页默认是可调整大小的。您必须在创建窗口(调用 DoModal
或 Create
)之前调用 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;
设置/获取窗格(树形和框架)的最小大小。SetPaneMinimumSize
或 SetPaneMinimumSizes
必须在创建窗口(调用 DoModal
或 Create
)之前调用,否则调用将被忽略并返回 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);
配置分隔器的宽度。您必须在创建窗口(调用 DoModal
或 Create
)之前调用 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;
显示树形时自动展开树形中的所有项。您必须在创建窗口(调用 DoModal
或 Create
)之前调用 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.h 和 PropPageFrameBordered.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_PAINT
和 WMU_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 上发布的组件。非常感谢:
- Sven Wiegand 的 CTreePropSheet 类。
- Robert A. T. Káldy 的 CSimpleSplitterWnd 类。
- Paolo Messina 的 Resizable library 类。
- PJ Naughter 的 CHookWnd 类。
- Keith Rule 的 CMemDC 类。
- lion 通过他的 CTreeCtrlEx 类给了我一个很好的开端。
感谢以下人员报告问题并提出修复建议:
- Don M 指出了 许多问题。
- Gabriele S 提出了 禁用页面功能的建议。
- pablo_mag 为 F1 键提供了支持。
- Przemek Miszczuk 修复了 额外的 OnKillActive 通知错误。
希望我没有遗漏任何人。
版本历史
- 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 很好地总结了该许可证,他说该许可证“允许在商业应用程序中使用。您只是不能将此作品作为库的一部分进行销售并声称是您自己的。这也意味着不需要注明出处,但注明出处会更好!”