Click here to Skip to main content
15,886,806 members
Articles / Desktop Programming / WTL

Resizable Property Sheet/Wizard using CDialogResize

Rate me:
Please Sign up or sign in to vote.
4.27/5 (9 votes)
12 Mar 20072 min read 87.6K   1.9K   30  
Make Property Sheet/Wizards resizable without much modification.
#pragma once

/* Resizable Property Sheet Implemenation based on CPropertySheetImpl by
Benjamin Kalytta (http://www.kalytta.com)
Version: 1.1 */

#define _WTL_NEW_PAGE_NOTIFY_HANDLERS

#if _WIN32_WINNT < 0x0501
#pragma message ("Double buffered windows using WS_EX_COMPOSITED requires Windows XP and higher, I'll set _WIN32_WINNT 0x0501")
#define _WIN32_WINNT 0x0501
#endif

#ifndef __ATLCRACK_H__
#include <atlcrack.h>
#endif

#define BEGIN_DLGRESIZE_MAP_EX(thisClass) \
	static void *ResizeMapStart; \
	static int ResizeMapIdx; \
	static const _AtlDlgResizeMap* GetDlgResizeMap() { \
		static _AtlDlgResizeMap ResizeMap[] = { 

#define ALT_DLGRESIZE_MAP_EX() \
		{ -1, 0 }, \

#define END_DLGRESIZE_MAP_EX() \
			{ -1, 0 }, \
			{ -1, 0 }, \
		}; \
		ResizeMapStart = ResizeMap; \
		return (_AtlDlgResizeMap*) (&ResizeMap[ResizeMapIdx]); \
	}

template <class T, class TBase = CPropertySheetWindow>
class ATL_NO_VTABLE CResizablePropertySheetImpl :
	public CPropertySheetImpl<T, TBase >,
	public CDialogResize<T>,
	public CMessageFilter {
private:
	bool EnableDoubleBuffering;
	bool IsWizard;
	bool IsDlgResizeInit;
	
	enum {
		ID_PSHEET_OK = IDOK,
		ID_PSHEET_APPLY = ID_APPLY_NOW,
		ID_PSHEET_CANCEL = IDCANCEL,
		ID_PSHEET_HELP = IDHELP,
		ID_PSHEET_TAB = ATL_IDC_TAB_CONTROL,
		ID_PSHEET_PREV = ID_WIZBACK,
		ID_PSHEET_NEXT = ID_WIZNEXT,
		ID_PSHEET_FINISH = ID_WIZFINISH,
		ID_PSHEET_BOTTOMFRAME = ID_WIZFINISH + 1,
		ID_PSHEET_TOPFRAME = ID_WIZFINISH + 2,
	};
	
public:

	CResizablePropertySheetImpl(ATL::_U_STRINGorID title = (LPCTSTR) NULL, UINT uStartPage = 0, HWND hWndParent = NULL, bool EnableDoubleBuffering = false, bool IsWizard = false) :
	  CPropertySheetImpl<T, TBase> (title, uStartPage, hWndParent), IsDlgResizeInit(false) {
		this->EnableDoubleBuffering = EnableDoubleBuffering;
		this->IsWizard = IsWizard;
		if (IsWizard) {
			SetWizardMode();
		}
	}

	static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam) {
		LPDLGTEMPLATE Template = (LPDLGTEMPLATE) lParam;

		if (uMsg == PSCB_PRECREATE) {
			
			// remove dialog border styles
			Template->style &= ~DS_MODALFRAME;

			// add child window and clipping styles
			Template->style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW;
			return 0;
		}
		return CPropertySheetImpl<T>::PropSheetCallback(hWnd, uMsg, lParam);
	}

	HWND Create(HWND hWndParent = NULL) {
		HWND Result = CPropertySheetImpl<T>::Create(hWndParent);
		
		ATLASSERT(m_hWnd != NULL); // Creation of Property Sheet failed !

		if (EnableDoubleBuffering) {
			SetWindowLongPtr(GWL_EXSTYLE, GetWindowLongPtr(GWL_EXSTYLE) | WS_EX_COMPOSITED);
		}
		DlgResize_Init();
		//IsDlgResizeInit = true;
		_Module.GetMessageLoop()->AddMessageFilter(this);
		return Result;
	}
	
	void SetWizardMode() {
		SetActiveResizeMap(1);
		IsWizard = true;
		return CPropertySheetImpl<T>::SetWizardMode();
	}

	void SetHeader(LPCTSTR szbmHeader) {
		SetActiveResizeMap(1);
		IsWizard = true;
		return CPropertySheetImpl<T>::SetHeader(szbmHeader);
	}

	void SetHeader(HBITMAP hbmHeader) {
		SetActiveResizeMap(1);
		IsWizard = true;
		return CPropertySheetImpl<T>::SetHeader(hbmHeader);
	}
	
	void SetWatermark(LPCTSTR szbmWatermark, HPALETTE hplWatermark = NULL) {
		SetActiveResizeMap(1);
		IsWizard = true;
		return CPropertySheetImpl<T>::SetWatermark(szbmWatermark, hplWatermark);
	}

	void SetWatermark(HBITMAP hbmWatermark, HPALETTE hplWatermark = NULL) {
		SetActiveResizeMap(1);
		IsWizard = true;
		return CPropertySheetImpl<T>::SetWatermark(hbmWatermark, hplWatermark);
	}

	/* This is required to handle modeless Dialog messages correctly */

	virtual BOOL PreTranslateMessage(LPMSG pMsg) {
		if (IsDialogMessage(pMsg)) {
			if (!::IsWindow(m_hWnd) || GetActivePage() == NULL) {
				/* Not really clean code, but better than creating an
				extra property sheet Message Loop class */
				PostQuitMessage(0);
			}
			return TRUE;
		}
		return FALSE;
	}

	/* --- CDialogResize enhancement --- */

	void SetActiveResizeMap(int MapIdx) {
		if (ResizeMapStart == NULL) {
			/* ResizeMapStart not yet initialized */
			GetDlgResizeMap();
		}
		int Idx = 0;
		bool WasMapEnd = false;
		while (true) {
			if (((_AtlDlgResizeMap*) ResizeMapStart)[Idx].m_nCtlID == -1 && ((_AtlDlgResizeMap*) ResizeMapStart)[Idx].m_dwResizeFlags == 0) {
				if (WasMapEnd) break;
				MapIdx--;
				if (MapIdx == 0) {
					ResizeMapIdx = Idx + 1;
					break;
				}
				WasMapEnd = true;
			} else {
				WasMapEnd = false;
			}
			Idx++;
		}
	}
	
	void OnSize(WPARAM wParam, CSize Size) {
		/* Resize Property Sheet controls manually first */
		BOOL Handled = FALSE;
		CDialogResize<T>::OnSize(WM_SIZE, 0, MAKELONG(Size.cx, Size.cy), Handled);
		UpdatePropertyPage(GetActivePage());
		SetMsgHandled(FALSE);
	}

	LRESULT OnSetActive(LPNMHDR Hdr) {
		UpdatePropertyPage((HWND) ((LPPSHNOTIFY) Hdr)->lParam);
		SetMsgHandled(FALSE);
		return TRUE;
	}

	BEGIN_MSG_MAP_EX(CResizablePropertySheetImpl)
		MSG_WM_SIZE(OnSize);
		NOTIFY_CODE_HANDLER_EX(PSN_SETACTIVE, OnSetActive)
		CHAIN_MSG_MAP(CPropertySheetImpl<T>)
		CHAIN_MSG_MAP(CDialogResize<T>)
	END_MSG_MAP()
	BEGIN_DLGRESIZE_MAP_EX(CResizablePropertySheetImpl)
		DLGRESIZE_CONTROL(ID_PSHEET_OK, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_APPLY, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_CANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_HELP, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_TAB, DLSZ_SIZE_X | DLSZ_SIZE_Y)
	ALT_DLGRESIZE_MAP_EX()
		DLGRESIZE_CONTROL(ID_PSHEET_PREV, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_NEXT, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_FINISH, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_CANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_HELP, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_TOPFRAME, DLSZ_SIZE_X)
		DLGRESIZE_CONTROL(ID_PSHEET_BOTTOMFRAME, DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(ID_PSHEET_BOTTOMFRAME, DLSZ_SIZE_X)
	END_DLGRESIZE_MAP_EX()
private:

	void UpdatePropertyPage(HWND hWnd) {

		/* Adjust property page size */
		CPropertyPageWindow Wnd = hWnd;
		CSize PageMargin;

		if (Wnd) {

			RECT rc, rctf, rcbf;
			GetClientRect(&rc);
			
			int Width = 0;
			int Height = 0;
			int Top = 0;
			int Left = 0;

			if (IsWizard) {

				GetDlgItem(ID_PSHEET_TOPFRAME).GetWindowRect(&rctf);
				GetDlgItem(ID_PSHEET_BOTTOMFRAME).GetWindowRect(&rcbf);
				ScreenToClient(&rctf);
				ScreenToClient(&rcbf);

				PageMargin.cx = 11;
				PageMargin.cy = 11;

				if (::GetProp(hWnd, _T("IsExterior"))) {
					Left = 0;
					Top = 0;
					Width = rc.right;
					Height = rcbf.top;
				} else {
					Top = rctf.top + PageMargin.cy;
					Left = PageMargin.cx;
					Width = rc.right - Left - PageMargin.cx;
					Height = rcbf.top - Top - PageMargin.cy;
				}
			} else {
				RECT rci;
				CTabCtrl Tab = GetTabControl();
				Tab.GetItemRect(HwndToIndex(Wnd), &rci);
				Tab.GetWindowRect(&rc);
				ScreenToClient(&rc);
				
				PageMargin.cx = 4;
				PageMargin.cy = 4;

				Top = rc.top + rci.bottom + PageMargin.cy;
				Left = rc.left + PageMargin.cx;
				Width = rc.right - PageMargin.cx - Left;
				Height = rc.bottom - PageMargin.cy - Top;
			}
			Wnd.SetWindowPos(NULL, Left, Top, Width, Height, SWP_NOACTIVATE | SWP_NOZORDER);
		}

	}

};

/* Static class members declaration and initialization */

template <class T, class TBase>
void *CResizablePropertySheetImpl<T, TBase>::ResizeMapStart = NULL;
template <class T, class TBase>
int CResizablePropertySheetImpl<T, TBase>::ResizeMapIdx = 0;

template <class T, class TBase = CPropertyPageWindow>
class ATL_NO_VTABLE CResizablePropertyPageImpl : public CPropertyPageImpl<T, TBase >, public CDialogResize<T> {
private:
	bool IsDoubleBufferEnabled;
public:
	CResizablePropertyPageImpl(ATL::_U_STRINGorID title = (LPCTSTR) NULL, bool IsExterior = false, bool EnableDoubleBuffering = false) :
	CPropertyPageImpl<T> (title) {
		if (IsExterior) m_psp.dwFlags |= PSP_HIDEHEADER;
		IsDoubleBufferEnabled = EnableDoubleBuffering;
	}
	
	LRESULT OnInitDialog(HWND hWnd, LPARAM lParam) {
		if (m_psp.dwFlags & PSP_HIDEHEADER) {
			SetProp(m_hWnd, _T("IsExterior"), (HANDLE) 1);
		}
		if (IsDoubleBufferEnabled) {
			SetWindowLongPtr(GWL_EXSTYLE, GetWindowLongPtr(GWL_EXSTYLE) | WS_EX_COMPOSITED);
		}
		DlgResize_Init(false, false);
		SetMsgHandled(FALSE);
		return FALSE;
	}

	void OnDestroy() {
		RemoveProp(m_hWnd, _T("IsExterior"));
		SetMsgHandled(false);
	}

	void EnableDoubleBuffering() {
		if (m_hWnd) {
			SetWindowLongPtr(GWL_EXSTYLE, GetWindowLongPtr(GWL_EXSTYLE) | WS_EX_COMPOSITED);
		} else {
			IsDoubleBufferEnabled = true;
		}
	}

	void DisableDoubleBuffering() {
		if (m_hWnd) {
			SetWindowLongPtr(GWL_EXSTYLE, GetWindowLongPtr(GWL_EXSTYLE) & ~WS_EX_COMPOSITED);
		} else {
			IsDoubleBufferEnabled = false;
		}
	}

	void AddPageFlags(UINT Flags) {
		m_psp.dwFlags |= Flags;
	}

	void RemovePageFlags(UINT Flags) {
		m_psp.dwFlags &= ~Flags;
	}

	BEGIN_MSG_MAP_EX(CResizablePropertyPageImpl)
		MSG_WM_INITDIALOG(OnInitDialog)
		MSG_WM_DESTROY(OnDestroy);
		/* forward WM_NOTIFY message to parent (Property Sheet) manually */
		if (uMsg == WM_NOTIFY) {
			/* since lParam in LPPSHNOTIFY struct isn't used, we'll use it */
			if (((LPNMHDR) lParam)->code == PSN_SETACTIVE) {
				((LPPSHNOTIFY) lParam)->lParam = (LPARAM) m_hWnd;
				::SendMessage(GetParent(), uMsg, wParam, lParam);
			}
		}
		CHAIN_MSG_MAP(CPropertyPageImpl<T>)
		CHAIN_MSG_MAP(CDialogResize<T>)
	END_MSG_MAP()
};

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Germany Germany
Hobby: system programing (operating system and hardware)
Prefered languages are x86 assembler, c and c++.
Currently student of applied computer science at university of applied sciences Bingen (Germany)

Comments and Discussions