Introduction
This article describes how to use WTL's CPropertySheetImpl
template within a resizable view. An overridden PropSheetCallback
changes the property sheet style for use as a view. A subclass of the property
sheet's tab control handles resizing of the property pages. The property sheet
and property pages use CDialogResize
to handle control resizing.
CPropertySheetImpl
is in the atldlgs.h
header file
while CDialogResize
is in atlframe.h
.
Property Sheet
Our new property sheet class, CPropView
, inherits from both
CPropertySheetImpl
and CDialogResize
to acquire
its desired behavior. The property sheet template provides an encapsulation of
the standard Windows property sheet control while the resize template provides a
mechanism for moving or resizing child controls.
However, standard property sheets are designed for use as modal or modeless
dialog windows, not views. To overcome the default behavior, override the
callback procedure and add a pre-creation message handler to change the window
style as shown below.
static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam)
{
if(uMsg == PSCB_PRECREATE)
{
LPDLGTEMPLATE lpDT = (LPDLGTEMPLATE)lParam;
lpDT->style -= DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU;
lpDT->style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
return 0;
}
else
return CPropertySheetImpl< CPropView >::PropSheetCallback(hWnd, uMsg, lParam); }
One other style change is required to complete the transition from standalone
dialog to view but there are two obstacles to overcome. The first obstacle is
that the style is an extended style, and it is not possible to change extended
styles in precreate. Secondly, the property sheet template does not process the
WM_INITDIALOG
message, which is where one might also change
extended styles.
Therefore, we created an initialization method. This method, _Init
,
modifies the extended style setting for the property sheet by setting
WS_EX_CLIENTEDGE
. It also initializes the dialog resize code and
subclasses the property sheet's tab control. _Init
is called by the
main frame's OnCreate()
handler just after the view is created.
Tab Control
The Windows property sheet uses an ordinary tab control to host property
pages. The tab control identifier is ATL_IDC_TAB_CONTROL
. In order
to resize the tab control, and subsequently, its property pages, add it to the
property sheet resize map as a sizable control. The property sheet buttons, not
shown in the example below, are also added to the resize map but as movable
controls.
BEGIN_DLGRESIZE_MAP(CPropView)
DLGRESIZE_CONTROL(ATL_IDC_TAB_CONTROL, DLSZ_SIZE_X | DLSZ_SIZE_Y)
END_DLGRESIZE_MAP()
Once the tab control is placed in the resize map, it will receive sizing
messages from the dialog resize code. However, one additional step is required,
since the messages are not relayed to the property pages. This is accomplished
with a template for the tab control that provides WM_WINDOWPOSCHANGED
message handling. Subclass the tab control in the _Init
method of
the property sheet class, CPropView
.
Here is the handler for the position changed message:
LRESULT OnWindowPosChanged(UINT, WPARAM, LPARAM lParam, BOOL&)
{
LPWINDOWPOS lpWP = (LPWINDOWPOS)lParam;
CPropertySheet m_sheet;
m_sheet.m_hWnd = GetParent();
::SetWindowPos(m_sheet.GetActivePage(), NULL, 0, 0, lpWP->cx - 8,
lpWP->cy - 25, SWP_NOMOVE);
m_sheet.m_hWnd = NULL;
return 0; }
Now the tab control resizes the active property page upon notice from the
property sheet resize handler. Other property pages are resized when they become
active. (Note: If you wish to control the minimum or maximum size that the view
or page can reach, see "Sidebar About Window Sizing" later in this article.)
Property Pages
Property pages also inherit from CDialogResize
so that their
child controls are resized. Although you could override the callback procedure
to change the page style, it is simpler to just pass in the appropriate window
style to the dialog resize initializer like this:
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
DlgResize_Init(false, false, WS_CHILD | WS_CLIPCHILDREN);
return 0; }
Add whatever controls or groups of controls you wish to resize or move to the
resize map, just as you would for any dialog using the dialog resize class.
Reminders
When working with property sheets and pages, remember:
- Create at least one property page in the constructor of the property sheet
- Some parameter changes must occur before the sheet or page is created
Also, please note that groupboxes do not peacefully coexist with the
WS_CLIPCHILDREN
style. If you intend to use them, remove the
clip children style from the property page's DlgResize_Init()
and
be prepared to suffer some screen flicker.
Sidebar About Window Sizing
There are certain window styles that inherently provide the capability to
limit minimum and maximum window size. For example, WS_THICKFRAME
and WS_CAPTION
. Compound styles that include either or both of
those styles, such as WS_OVERLAPPEDWINDOW
, provide the capability
as well. These styles receive the WM_GETMINMAXINFO
message whenever
they are moved or sized.
You can enable mimimum (or maximum) size tracking in any window with those
styles, such as main frame, by initializing the appropriate member variable in
the constructor like this:
POINT m_ptMinTrackSize;
CMainFrame()
{
m_ptMinTrackSize.x = -1;
m_ptMinTrackSize.y = -1; }
Next, add an entry to the message map for WM_GETMINMAXINFO
and
add the following message handler:
LRESULT OnGetMinMaxInfo(UINT, WPARAM, LPARAM lParam, BOOL&)
{ if (m_ptMinTrackSize.x != -1 && m_ptMinTrackSize.y != -1)
{
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
lpMMI->ptMinTrackSize = m_ptMinTrackSize;
}
return 0; }
Finally, set m_ptMinTrackSize.x
and
m_ptMinTrackSize.y
to your desired minimum values in the
OnCreate
handler.
For window styles that do not inherently support minimum size tracking, use a
handler for the WM_WINDOWPOSCHANGING
message to achieve similar
results, as shown below. Set your x and y track values in the create or
initialize handler as appropriate.
LRESULT OnWindowPosChanging(UINT, WPARAM, LPARAM lParam, BOOL&)
{
LPWINDOWPOS lpWP = (LPWINDOWPOS)lParam;
if (lpWP->cx <= m_ptMinTrackSize.x) lpWP->cx = m_ptMinTrackSize.x;
if (lpWP->cy <= m_ptMinTrackSize.y) lpWP->cy = m_ptMinTrackSize.y;
return 0; }
Terms Of Use
The sample project and property sheet/page classes available with this
article are free. Use them however you wish.
THIS SOFTWARE IS DISTRIBUTED AS-IS, WITHOUT WARRANTIES OF ANY KIND.