![]() |
Desktop Development »
Tabs & Property Pages »
General
Intermediate
License: The Code Project Open License (CPOL)
CTreePropSheetEx – an extended version of CTreePropSheetBy Yves TkaczykCTreePropSheetEx is an extension of CTreePropSheet offering new features such as resizing, skipping empty pages, and new property frames such as Office 2003 option sheet. |
VC6, VC7, VC7.1, VC8.0Win2K, WinXP, Win2003, MFC, VS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
||||||||||||||||||

CTreePropSheetOffice2003 for Office 2003 mode.

Resizing CTreePropSheetEx with XP theme enabled.

CTreePropSheetBordered, a CTreePropSheetEx derived class with bordered page frame, XP theme non enabled.
CTreePropSheetEx is an evolution above the excellent CTreePropSheet[^] written by Sven Wiegand. It offers the following additional features over the initial class:
CPropPageFrameEx. It introduces flicker free gradient caption. It also offers the option to use the GDI GradientFill function.
CPropPageFrameBordered. Based on CPropPageFrameEx, this class draws a frame around the page even for non-themed applications (see sample picture). This set of classes uses many components that have been published on CodeProject. See the Acknowledgements section for a full list of the used components.
Using CTreePropSheetEx is very similar to using CPropertySheet or CTreePropSheet. At this point, if you haven�t already done so, I would recommend that you read Sven�s article regarding CTreePropSheet [^]. In the following explanation, I will assume that you already have a property sheet and its pages created. To use the class, you need to:
|
File names (37 files) |
Description |
|---|---|
| TreePropSheetEx.h TreePropSheetEx.cpp |
CTreePropSheetEx class. |
| TreePropSheetBordered.h TreePropSheetBordered.cpp |
CTreePropSheetBordered class, derived from CTreePropSheetEx, draws a border around the frame when XP theme is not available. |
| TreePropSheetOffice2003.h TreePropSheetOffice2003.cpp |
CTreePropSheetOffice2003 class, derived from CTreePropSheetEx, provides a look and feel similar to Office 2003 option dialogs. |
| TreePropSheetBase.h TreePropSheetBase.cpp |
CTreePropSheetBase class, base class for CTreePropSheetEx. |
| TreePropSheetTreeCtrl.h TreePropSheetTreeCtrl.cpp |
CTreePropSheetTreeCtrl class, CTreeCtrl derived allowing custom color for each tree item. Based on CTreeCtrlEx[^]. |
| PropPageFrame.h PropPageFrame.cpp |
CPropPageFrame class. Abstract base class for the frame drawn around pages when in tree mode. |
| PropPageFrameEx.h PropPageFrameEx.cpp |
CPropPageFrameEx class. CPropPageFrame implementation that provides flicker-free frame. It is the class used by default by CTreePropSheetBase and CTreePropSheetEx. |
| PropPageFrameBordered.h PropPageFrameBordered.cpp |
CPropPageFrameBordered class. Derived from CPropPageFrameEx, extends it by drawing a border around the frame when XP theme is not available. |
| PropPageFrameOffice2003.h PropPageFrameOffice2003.cpp |
CPropPageFrameOffice2003 class. Derived from CPropPageFrameEx, extends it by providing a look and feel similar to Office 2003 option dialogs. |
| TreePropSheetSplitter.h TreePropSheetSplitter.cpp |
CTreePropSheetSplitter class. Based on CSimpleSplitterWnd[^], implements the splitter class. |
| TreePropSheetUtil.hpp | Misc. utility classes. |
| ThemeLibEx.h ThemeLibEx.cpp |
Helper class for accessing XP theme functions. |
| TreePropSheetResizableLibHook.h TreePropSheetResizableLibHook.cpp |
Implements the resizable library using message hooking rather than inheritance. |
| ResisableGrip.h ResizableGrip.cpp ResizableLayout.h ResizableLayout.cpp ResizableMinMax.h ResizableMinMax.cpp ResizableMsgSupport.h ResizableMsgSupport.inl ResizableState.h ResizableState.cpp |
Resizable library[^] classes. |
| Hookwnd.h Hookwnd.cpp |
CHookWnd[^] class. Defines the interface for an MFC class to implement message hooking. |
| memDC.h | CMemDC[^] class. Implements a memory Device Context which allows flicker free drawing. |
| HighColorTab.hpp | Dynamically updates the tab�s image list to add 16M color icons support. |
| ResizablePage.h ResizablePage.cpp |
Resizable library class for property pages[^]. Not directly used by |
CTreePropSheetEx, CTreePropSheetBordered or CTreePropSheetOffice2003 instead of CPropertySheet. If you already have a class deriving from CPropertySheet, you need to derive from CTreePropSheetEx (or CTreePropSheetBordered or CTreePropSheetOffice2003) instead. You also need to replace all references to CPropertySheet by CTreePropSheetEx (or CTreePropSheetBordered or CTreePropSheetOffice2003). If you are using a CPropertySheet object directly, just change its type to CTreePropSheetEx (or CTreePropSheetBordered or CTreePropSheetOffice2003).
Note: CTreePropSheetEx, CTreePropSheetBordered and CTreePropSheetOffice2003 are in the namespace TreePropSheet.
Note: CHookWnd uses CCriticalSection. This class is defined in <afxmt.h>, therefore you should add the following line to your precompiled header file:
#include <afxmt.h>
Also, CPropPageFrame uses dynamic_cast which means that RTTI support should be enabled.
CResizablePage class. The following code snippet demonstrates how this is done for one of the property pages in the sample application:
CPropertyPage to CResizablePage.
OnInitDialog method of each page in order to add the sizing constraints for the controls inside the page. For instance, in one of the pages in the demo, this looks like this: 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, you need to customize each property page in order to draw a white background (or more accurately, a background with the system color COLOR_WINDOW). To do so, you need to handle WM_CTLCOLOR for each property page. This message is sent by each child control to the parent page before it is drawn and let the parent prepare the DC before the control is rendered. The new message handler should be as follows: HBRUSH CPageDates::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
pDC->SetBkMode(TRANSPARENT);
return ::GetSysColorBrush( COLOR_WINDOW );
}
This code is a minimal default implementation. You can look at the demo project to see how it is possible to update property pages so that they can be rendered in the 'standard mode' and the Office 2003 mode.
A full reference of the API has been generated using Doxygen[^]. A link is provided at the top of this document.
All the code relating to CTreePropSheetEx is placed in the namespace TreePropSheet. This is the same namespace as for CTreePropSheet. Some of the other classes used by this framework live in their own namespace or in the global namespace.
Two features are set at compile time:
#define USE_OWNER_DRAW_GRADIANT
This will give you better compatibility on older operating systems (Win 95 and Win NT 4.0).
#define XPSUPPORTCTreePropSheetBase();
CTreePropSheetBase(UINT nIDCaption,
CWnd* pParentWnd = NULL,
UINT iSelectPage = 0);
CTreePropSheetBase(LPCTSTR pszCaption,
CWnd* pParentWnd = NULL,
UINT iSelectPage = 0);
The constructors are overrides of the CPropertySheet constructors.
BOOL SetTreeViewMode(BOOL bTreeViewMode = TRUE,
BOOL bPageCaption = FALSE,
BOOL bTreeImages = FALSE);
Sets the sheet in tree mode and specifies whether the page caption should be displayed. Also indicates if the tree items should have icons.
bool SetIsResizable(const bool bIsResizable); bool IsResizable() const;
Enable/disable the sheet resizing feature. The sheet is resizable by default. You must call SetIsResizable before creating the window (call to DoModal or Create) or this call will be ignored and will return false.
void SetMinSize(const CSize& sizeMin); CSize GetMinSize() const;
Set/get the minimum size for the sheet.
void SetMaxSize(const CSize& sizeMax); CSize GetMaxSize() const;
Set/get the maximum size for the sheet.
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;
Set/get the minimum sizes for the panes (the tree and the page). SetPaneMinimumSize or SetPaneMinimumSizes must be called before creating the window (call to DoModal or Create) or this call will be ignored and will return false.
SetPaneMinimumSize sets the tree and frame minimum size to the value whereas SetPaneMinimumSizes allows to set individual minimum size values for the tree and the frame.
Note: Since version 0.2, it is recommended to use GetPaneMinimumSizes rather than GetPaneMinimumSize as this method does not support the individual minimum sizes and returns -1 when it is called under this scenario.
void SetTreeIsResizable(const bool bIsTreeResizable); bool IsTreeResizable() const;
Allow/disallow resizing the tree width with the mouse. The tree is still resizable programmatically as long as the dialog is resizable (IsResizable returns true). The cursor splitter is updated according to the tree resizing capability.
int GetSplitterWidth() const;
bool SetSplitterWidth(const int nSplitterWidth);
Configure the splitter width. You must call SetSplitterWidth before creating the window (call to DoModal or Create) or this call will be ignored and will return false.
void SetTreeResizingMode(const enTreeSizingMode eSizingMode);
enTreeSizingMode GetTreeResizingMode() const;
Describes how the tree should be resized when the sheet is resized. The options are:
TSM_Fixed: The tree width is unchanged when the property sheet is resized. One exception to this is, if the property page has reached its minimum size (minimum pane size) then the tree will shrink.
TSM_Proportional: The tree grows and shrinks proportionally to the property sheet. virtual BOOL SetTreeWidth(int nWidth); int GetTreeWidth() const;
Set/get the tree width. Note that the tree width can now be changed even when the sheet windows have been created. However, the sheet has to be resizable (IsResizable returns true).
void SetRealTimeSplitter(const bool bRealtime);
bool IsRealTimeSplitter() const;
Define whether the panes (tree and frame) should be resized in real-time when the user drags the splitter.
void SetSkipEmptyPages(const bool bSkipEmptyPages); bool IsSkippingEmptyPages() const;
Allow/disallow display of empty pages.
void SetEmptyPageText(LPCTSTR lpszEmptyPageText);
DWORD SetEmptyPageTextFormat(DWORD dwFormat);
Set the text content and format for the text to be displayed in empty pages.
BOOL SetTreeDefaultImages(CImageList *pImages);
BOOL SetTreeDefaultImages(UINT unBitmapID, int cx,COLORREF crMask);
Set the icons for the empty pages and pages without icons.
void SetContextMenuMode(const enContextMenuMode eContextMenuMode); TreePropSheet::enContextMenuMode GetContextMenuMode() const;
Define how the help context menus should be handled. The Win32 property sheet displays a 'What's This?' context menu when the user right-clicks on the different controls on the sheet, and provides some appropriate help for the controls that it �owns� such as the OK and Cancel buttons. For the controls added to the sheet (like the tree and the frame), a generic message is shown indicating that no help topic is associated with the item. Since the message is irrelevant, it makes sense not to display the menu at all for these controls. If you have implemented help for these controls, set the value to TPS_All so that the messages are not trapped.
The options are:
TPS_All: Always display the context menu.
TPS_PropertySheetControls: Display context menu only for the property sheet controls, that is, the menu is not displayed for the tree and frame.
TPS_None: Never display the context menu. void SetAutoExpandTree(const bool bAutoExpandTree); bool IsAutoExpandTree() const;
Automatically expand all the items in the tree when it is displayed. You must call SetAutoExpandTree before creating the window (call to DoModal or Create) or this call will have no effect.
bool EnablePage(const CPropertyPage* const pPage, const bool bEnable); bool IsPageEnabled(const CPropertyPage* const pPage) const;
The first method allows enabling or disabling the specified property page. The second returns the enabled status of the specified page. It is also possible to set the enable status of a page by sending the following message to the property sheet:
Message ID: WMU_ENABLEPAGE (defined in TreePropSheetBase.h) WPARAM: Pointer to property page LPARAM: Enable/disable state
A helper method has also been written to send the message. hWndTarget is the hWnd of the property sheet.
inline BOOL Send_EnablePage(HWND hWndTarget, CPropertyPage* pPage, const bool bEnable) { ASSERT(::IsWindow(hWndTarget)); return (0 != ::SendMessage(hWndTarget, WMU_ENABLEPAGE, (WPARAM)pPage, (LPARAM)bEnable)); }
The handle internally calls EnablePage.
Below is a class diagram showing some of the classes used by CTreePropSheetEx.

All the internal objects used by the class are created using a factory method. This lets you use another class if necessary. The methods are:
virtual CTreeCtrl* CreatePageTreeObject(); virtual CPropPageFrame* CreatePageFrame(); virtual tSplitterPtr CreateSplitterObject() const; virtual tLayoutManagerPtr CreateLayoutManagerObject();
The following methods let you retrieve the internal object used by the class:
CTreeCtrl* GetPageTreeControl(); CPropPageFrame* GetFrameControl(); virtual tSplitter* GetSplitterObject() const; virtual CWnd* GetSplitterWnd() const; virtual tLayoutManager GetLayoutManagerObject() const;
As mentioned above, CPropPageFrameEx implements double-buffering to reduce flickering. Also, the class has a new virtual method DrawBackground which as you might have guessed is called when the frame background needs redrawing. CPropPageFrameBordered overrides this method to display a border on platforms that do not support XP themes. The method signature is:
virtual void DrawBackground(CDC* pDC);
Also, CPropPageFrameEx now owns and exposes the XP theme helper class so that it can be used by the derived classes. The method to access the helper class is:
const CThemeLibEx& GetThemeLib() const;
The bordered frame (CPropPageFrameBordered located in PropPageFrameBordered.h and PropPageFrameBordered.cpp) is not used by default. It allows drawing a frame around the active property page when the application does not use XP themes. If you want to use this feature, you need to create a new class that inherits from CTreePropSheetEx (or CTreePropSheet for that matter), include PropPageFrameBordered.h in your implementation file, and override the method CreatePageFrame as follows:
CPropPageFrame* CYourNewClassName::CreatePageFrame()
{
return new CPropPageFrameBordered;
}
The main reason for creating CTreePropSheetBase rather than deriving from CTreePropSheet was that I needed to update the class (mainly to change the access of some methods and properties). I also made a few fixes to the code. Having the new class also means that you can start using CTreePropSheetEx without changing any of your code that is using CTreePropSheet.
Since CThemeLib was inside the CPropPageFrameDefault.cpp file, it was not possible to reuse this code which is why I had to extract it out of this file. CThemeLibEx is identical to the original class in functionality. I have also changed the constness of the methods in order to be able to return a const reference to the class.
The layout manager is integrated with the property sheet using sub-classing rather than inheritance. This way, it is possible to make resizing an option at runtime. When choosing not to have any resizing feature, no layout manager or splitter objects are created.
Implementing this feature was straightforward except for the keyboard handling. All the processing is performed in CTreePropSheetBase::PreTranslateMessage where we can intercept the key strokes before they get routed to the tree control.
Enabling or disabling a property page must be done via the property sheet API (call to EnablePage) or by sending a WMU_ENABLEPAGE message. Another solution would have been to add a hook to each property page when they are added to the sheet and handle the WM_ENABLE message to perform appropriate action. While I believe this is feasible, I went against it because I don't think that many applications are enabling and disabling property pages (I might be wrong) mainly because the sheet control does not give any visual feedback to the user when a page is disabled.
The implementation of the feature required using an extended version of the tree control. This version allows setting the color of a tree item by calling SetItemColor or sending a WMU_ENABLETREEITEM message. Using the message mechanism means that the control is still stored as a CTreeCtrl by the property sheet (in CTreePropSheetBase). If you already have a custom tree control, you will need to handle WM_PAINT and WMU_ENABLETREEITEM in your implementation.
I have tested the sample program on Windows 2000/XP and 2003 Server. I will try to test it on Windows 95, 98 and NT 4.0 when I can have access to these platforms.
I have compiled the code on Visual C++ 6.0, Visual Studio 2003 and Visual Studio 2005 Beta.
Win 98 or later and Windows 2000 or later for GDI gradient support.
If you want to enable XP theme, you will also need the platform SDK to compile.
CTreePropSheetEx uses many components that have been published on CodeProject. Many thanks to:
Thank you to the following persons for reporting issues and suggesting fixes:
I hope I haven't forgotten anybody.
CTreePropSheetBordered class added.
CTreePropSheetOffice2003 class added.
CTreePropSheetBase. The files in the article are released under the terms of the Artistic license. You may obtain a copy of the License here.
Paolo Messina sums up the license quite well by saying that the license �allows for use in commercial applications. You just can't sell this work as part of a library and claim it's yours. This also means that credits are not required, but they would be nice!�
| You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||