A resizable property sheet within a view

I have an SDI application, where the main view consists of a CTabCtrl. In order to avoid handling the page changes myself, I decided to try to use a CPropertySheet instead. This gave me some problems handling resizing of the property sheet, when the user resizes the view.
Microsoft Knowledge Base article Q143291 gave a partial answer: How to resize the CPropertySheet at startup.

I tried to manipulate the code from Q143291 so it did what I wanted. And it worked! It flickers quite a lot though, but it is good enough for me.

Please study Q143291 as well. You may find a better way of doing this. All the resizing stuff is in the subclassed CPropertySheet. And that is kind of neat, IMHO.

I have moved most of the stuff from OnInitDialog into Resize, and added

	pParent = GetParent();
	pParent->GetClientRect(&rectParent);
to the subclassed CPropertySheet Resize. The rest of the CPropertySheet stuff is from Q143291. The view stuff is mine.

Note that I don't use OK / Cancel / Apply buttons. It should be a trivial matter to move these buttons in OnInitDialog as well. You can see how to do this in Q143291.

How do you implement a resizable property sheet within a view? Check this:
1. Subclass CPropertySheet.
2. Use only one constructor:

CResizablePropertySheet::CResizablePropertySheet(CWnd* pParentWnd)
	:CPropertySheet(AFX_IDS_APP_TITLE, pParentWnd)
{
	// AddPage here
}
3. Add protected CRect m_rectPage.
4. Create public member Resize():
void CResizablePropertySheet::Resize()
{
	// Find parent
	CWnd* pParent;
	CRect rectParent;

	pParent = GetParent();
	if (pParent == NULL)
		{
		AfxMessageBox("Cannot resize property sheet. Sheet has no parent", MB_ICONEXCLAMATION | MB_OK | MB_APPLMODAL);
		return;
		}

	// Get parents client area
	pParent->GetClientRect(&rectParent);

	// Resize the sheet
	// First find relative change
	CSize sizeRelChange;
	CRect rectWindow;

	GetWindowRect(&rectWindow);
	ScreenToClient(&rectWindow);
	sizeRelChange.cx = rectWindow.Width() - rectParent.Width();
	sizeRelChange.cy = rectWindow.Height() - rectParent.Height();

	rectWindow.right -= sizeRelChange.cx;
	rectWindow.bottom -= sizeRelChange.cy;
	// Then resize the sheet
	MoveWindow(&rectWindow);

	// Resize the CTabCtrl
	CTabCtrl* pTab = GetTabControl();
	ASSERT(pTab);
	pTab->GetWindowRect(&rectWindow);
	ScreenToClient(&rectWindow);
	rectWindow.right -= sizeRelChange.cx;
	rectWindow.bottom -= sizeRelChange.cy;
	pTab->MoveWindow(&rectWindow);

	// Resize the active page
	CPropertyPage* pPage = GetActivePage();
	ASSERT(pPage);
	// Store page size in m_rectPage
	pPage->GetWindowRect(&m_rectPage);
	ScreenToClient(&m_rectPage);
	m_rectPage.right -= sizeRelChange.cx;
	m_rectPage.bottom -= sizeRelChange.cy;
	pPage->MoveWindow(&m_rectPage);
}
5. Overload OnInitDialog:
BOOL CResizablePropertySheet::OnInitDialog() 
{
	BOOL bResult = CPropertySheet::OnInitDialog();

	Resize();

	return bResult;
}

6. Define user-defined message WM_RESIZEPAGE (WM_USER + 111 or something). Add ON_MESSAGE(WM_RESIZEPAGE, OnResizePage) to the message map. Remember to define afx_msg LRESULT OnResizePage(WPARAM wParam, LPARAM lParam) in the header file. OnResizePage:

LONG CResizablePropertySheet::OnResizePage(UINT, LONG)
	{
	// Resize the page using m_rectPage, which was set in
	// Resize().
	CPropertyPage* pPage = GetActivePage();
	ASSERT(pPage);
	pPage->MoveWindow(&m_rectPage);

	return 0;
	}

7. Handle ID_APPLY_NOW (add ON_COMMAND(ID_APPLY_NOW, OnApplyNow) to the message map. Remember to define afx_msg void OnApplyNow() in the header file).
OnApplyNow:

void CResizablePropertySheet::OnApplyNow()
	{
	// The sheet resizes the page whenever the apply button is clicked.
	// So we need to resize it to what we want.
	PostMessage(WM_RESIZEPAGE);
	}
8. Overload OnNotify:
BOOL CResizablePropertySheet::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
{
	NMHDR* pNMHDR = (LPNMHDR) lParam;
	
	// The sheet resizes the page whenever it is activated
	// so we need to resize it to what we want
	if (pNMHDR->code == TCN_SELCHANGE)
		// user-defined message needs to be posted, not sent, because
		// page must be resized after TCN_SELCHANGE has been processed.
		PostMessage(WM_RESIZEPAGE);

	return CPropertySheet::OnNotify(wParam, lParam, pResult);
}
9. Add private member
CResizablePropertySheet* m_pResizablePropertySheet;

to your CView-derived class. Initialize this member to NULL in the constructor.
10. Override OnInitialUpdate in your CView-derived class:

void CResizableView::OnInitialUpdate() 
	{
	CView::OnInitialUpdate();

	// create the property sheet
	m_pResizablePropertySheet = new CResizablePropertySheet(this);
	if (!m_pResizablePropertySheet->Create(this, 
		WS_CHILD | WS_VISIBLE, 0))
		{
		TRACE("Cannot create property sheet!\n");
		delete m_pResizablePropertySheet;
		m_pResizablePropertySheet = NULL;
		return;
		}
	}
11. Handle WM_SIZE in your CView-derived class:
void CResizableView::OnSize(UINT nType, int cx, int cy) 
	{
	CView::OnSize(nType, cx, cy);

	// Resize property sheet
	if (m_pNetDefPropertySheet != NULL)
		m_pResizablePropertySheet->Resize();
	}

Last updated: 21 July 1998