![]() |
Desktop Development »
Tabs & Property Pages »
Tabs and Property Pages
Intermediate
WTL CPropertySheet as a Resizable ViewBy Ed GadziemskiHow to use WTL's CPropertySheet implementation as a resizable view instead of a modal or modeless dialog |
VC6Win2K, WinXP, WTL, Dev
|
|
|
|
||||||||||||||||
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.
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) { // dialog template is available and changeable pre-creation if(uMsg == PSCB_PRECREATE) { LPDLGTEMPLATE lpDT = (LPDLGTEMPLATE)lParam; // remove dialog border styles lpDT->style -= DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU; // add child window and clipping styles lpDT->style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; return 0; } else // call inherited method to handle PSCB_INITIALIZED 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.
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&)
{ // get window position structure from lParam
LPWINDOWPOS lpWP = (LPWINDOWPOS)lParam;
// initialize a property sheet variable with the parent's handle
CPropertySheet m_sheet;
m_sheet.m_hWnd = GetParent();
// set the size of the active property page to slightly smaller
// than the tab control's new client area size
::SetWindowPos(m_sheet.GetActivePage(), NULL, 0, 0, lpWP->cx - 8,
lpWP->cy - 25, SWP_NOMOVE);
// release the property sheet handle since we don't own it
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 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&)
{ // init resize code. No gripper, no min size tracking, Child window style
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.
When working with property sheets and pages, remember:
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.
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()
{ // initialize minimum size tracking variables
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&)
{ // get window position structure from lParam
LPWINDOWPOS lpWP = (LPWINDOWPOS)lParam;
// don't allow resizing below minimum x
if (lpWP->cx <= m_ptMinTrackSize.x) lpWP->cx = m_ptMinTrackSize.x;
// don't allow resizing below minimum y
if (lpWP->cy <= m_ptMinTrackSize.y) lpWP->cy = m_ptMinTrackSize.y;
return 0; }
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.
6 messages have been posted for this article.
Visit http://www.codeproject.com/KB/tabs/propview.aspx to post and view comments
on this article, or click
here to get a
print view with messages.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 31 Mar 2002 Editor: Chris Maunder |
Copyright 2002 by Ed Gadziemski Everything else Copyright © CodeProject, 1999-2010 Web22 | Advertise on the Code Project |