WTL CPropertySheet as a Resizable View

WTL CPropertySheet as a Resizable View

 
CPropertySheet Sample Image  CPropertySheet Sample Image

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)
{ // 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.

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&)
{ // 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

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.

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()
{ // 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; }

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.

License

posted @ 2022-12-13 14:51  小风风的博客  阅读(28)  评论(0编辑  收藏  举报