Click here to Skip to main content
15,881,172 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.5K   1.9K   30  
Make Property Sheet/Wizards resizable without much modification.
#ifndef __ATL_RESIZABLE_PROPERTY_SHEET_H__ 
#define __ATL_RESIZABLE_PROPERTY_SHEET_H__ 

#pragma once

// Resizable Property Sheet Implemenation based on CPropertySheetImpl by
// Benjamin Kalytta (http://www.kalytta.com)
// Version: 1.2
// Also added modification by free2000fly (http://www.codeproject.com)

#ifndef _WTL_NEW_PAGE_NOTIFY_HANDLERS 
#define _WTL_NEW_PAGE_NOTIFY_HANDLERS 
#endif  // _WTL_NEW_PAGE_NOTIFY_HANDLERS 

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

namespace WTL { 

#define BEGIN_DLGRESIZE_MAP_EX(thisClass) \
	void StaticResizeMap() {

#define DLGRESIZE_CONTROL_EX(ControlId, ResizeFlags) \
		InitialMap.Add(_AtlDlgResizeMapEx(ControlId, ResizeFlags));

#define END_DLGRESIZE_MAP_EX \
		InitialMap.Add(_AtlDlgResizeMapEx(-1, 0)); \
	}

template <class T>
class CDialogResizeDynamic : public CDialogResize<T> {
private:
	bool IsInitialized;
public:

	/* Same class as _AtlDlgResizeMap only with ctor, should be added in WTL some day */

	struct ATL_NO_VTABLE _AtlDlgResizeMapEx {
		int m_nCtlID;
		DWORD m_dwResizeFlags;
		_AtlDlgResizeMapEx(int ControlId, DWORD ResizeFlags) : m_nCtlID(ControlId), m_dwResizeFlags(ResizeFlags) {
		}
	};

	ATL::CSimpleArray<_AtlDlgResizeMapEx> InitialMap;

	/* Import all entries from static resize map here */

	CDialogResizeDynamic() : CDialogResize<T>(), IsInitialized(false) {
		T* pT = static_cast<T*>(this);

		InitialMap.RemoveAll();
		pT->StaticResizeMap();
	}

	const _AtlDlgResizeMap* GetDlgResizeMap() {
		return (_AtlDlgResizeMap*) InitialMap.GetData(); // force conversion
	}

	void DlgResize_Init(bool bAddGripper = true, bool bUseMinTrackSize = true, DWORD dwForceStyle = WS_CLIPCHILDREN) {
		CDialogResize<T>::DlgResize_Init(bAddGripper, bUseMinTrackSize, dwForceStyle);
		IsInitialized = true;
	}
	
	void ResetResizeMap() {
		InitialMap.RemoveAll();
	}

	// Groups are not allowed if DialogResize is already initialized

	void AddToResizeMap(int ControlId, DWORD ResizeFlags) {
		T* pT = static_cast<T*>(this);
		if (IsInitialized) {
			ATLASSERT(ControlId != -1);
			ATL::CWindow ctl = pT->GetDlgItem(ControlId);
			ATLASSERT(ctl.IsWindow());
			
			RECT rectCtl = { 0 };
			ctl.GetWindowRect(&rectCtl);
			::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT) &rectCtl, 2);

			_AtlDlgResizeData data = {ControlId, ResizeFlags, { rectCtl.left, rectCtl.top, rectCtl.right, rectCtl.bottom } };
			m_arrData.Add(data);
		} else {
			if (InitialMap.GetSize() == 0) {
				InitialMap.Add(_AtlDlgResizeMapEx(ControlId, ResizeFlags));
			} else {
				InitialMap.SetAtIndex(InitialMap.GetSize() - 1, _AtlDlgResizeMapEx(ControlId, ResizeFlags));
			}
			InitialMap.Add(_AtlDlgResizeMapEx(-1, 0));
		}
	}

	void RemoveFromResizeMap(int ControlId) {
		if (IsInitialized) {
			ATLASSERT(ControlId != -1);
			for (int i = 0; i < m_arrData.GetSize(); i++) {
				if (m_arrData[i].m_nCtlID == ControlId) {
					m_arrData.RemoveAt(i);
					break;
				}
			}
		} else {
			for (int i = 0; i < InitialMap.GetSize(); i++) {
				if (InitialMap[i].m_nCtlID == ControlId) {
					InitialMap.RemoveAt(i);
					break;
				}
			}
		}
	}

	void ChangeResizeMapEntry(int ControlId, DWORD ResizeFlags) {
		if (IsInitialized) {
			ATLASSERT(ControlId != -1);
			for (int i = 0; i < m_arrData.GetSize(); i++) {
				if (m_arrData[i].m_nCtlID == ControlId) {
					m_arrData[i].m_dwResizeFlags = ResizeFlags;
					break;
				}
			}
		} else {
			for (int i = 0; i < InitialMap.GetSize(); i++) {
				if (InitialMap[i].m_nCtlID == ControlId) {
					InitialMap[i].m_dwResizeFlags = ResizeFlags;
					break;
				}
			}
		}
	}
#if 0
	void AddToResizeMapWithGroup(_AtlDlgResizeMapEx Map[]) {

	}
#endif
};

/* -------------------------------------------------------------------------------- */

template  <class T, class TBase = CPropertySheetWindow> 
class ATL_NO_VTABLE CResizablePropertySheetImpl
	: public CPropertySheetImpl <T, TBase>,
	public CDialogResizeDynamic <T>,
	public CMessageFilter
{ 
private:
	bool EnableDoubleBuffering;
	bool IsWizard;
	
	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) 
    {
		this->EnableDoubleBuffering = EnableDoubleBuffering;

		if (IsWizard) {
			SetWizardMode(true);
		}
	}

	static int CALLBACK PropSheetCallback(HWND hWnd, UINT uMsg, LPARAM lParam) {
		if (uMsg == PSCB_PRECREATE) { 
            LPDLGTEMPLATE Template = (LPDLGTEMPLATE) lParam;
			// remove dialog border styles
			Template->style &= ~DS_MODALFRAME;

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

    void OnSheetInitialized() {
        _Module.GetMessageLoop()->AddMessageFilter(this); 
    }

    virtual void OnFinalMessage(HWND hWnd) { 
        CMessageLoop* pLoop = _Module.GetMessageLoop(); 
        ATLASSERT(pLoop != NULL);
        pLoop->RemoveMessageFilter(this); 
    } 

    void SetWizardMode(bool IsWizard) {
		ResetResizeMap();
		if (IsWizard) {
			AddToResizeMap(ID_PSHEET_PREV, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_NEXT, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_FINISH, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_CANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_HELP, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_TOPFRAME, DLSZ_SIZE_X);
			AddToResizeMap(ID_PSHEET_BOTTOMFRAME, DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_BOTTOMFRAME, DLSZ_SIZE_X);
		} else {
			AddToResizeMap(ID_PSHEET_OK, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_APPLY, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_CANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_HELP, DLSZ_MOVE_X | DLSZ_MOVE_Y);
			AddToResizeMap(ID_PSHEET_TAB, DLSZ_SIZE_X | DLSZ_SIZE_Y);
		}
		this->IsWizard = IsWizard;
		CPropertySheetImpl<T>::SetWizardMode();
	}

	void SetHeader(LPCTSTR szbmHeader) {
		SetWizardMode(true);
		CPropertySheetImpl<T>::SetHeader(szbmHeader);
	}

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

	void SetWatermark(HBITMAP hbmWatermark, HPALETTE hplWatermark = NULL) {
		SetWizardMode(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;
	}

	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;
	} 

    LRESULT OnWmShowWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { 
		DlgResize_Init();
#if _WIN32_WINNT >= 0x0501
		if (EnableDoubleBuffering) {
			SetWindowLongPtr(GWL_EXSTYLE, GetWindowLongPtr(GWL_EXSTYLE) | WS_EX_COMPOSITED);
		}
#endif
		bHandled = FALSE; 
		return S_OK; 
    } 
	
	LRESULT OnEraseBackground(HDC hDc) {
		
		/* Helps prevent flickering during resizing */

		if (IsWizard && GetActivePage()) {
			RECT rcTop, rcWnd, rcPage;

			GetDlgItem(ID_PSHEET_TOPFRAME).GetWindowRect(&rcTop);
			ScreenToClient(&rcTop);

			CWindow(GetActivePage()).GetWindowRect(&rcPage);
			ScreenToClient(&rcPage);

			ExcludeClipRect(hDc, rcPage.left, rcPage.top, rcPage.right, rcPage.bottom);
			ExcludeClipRect(hDc, rcTop.left, 0, rcTop.right, rcTop.bottom);
			
			GetClientRect(&rcWnd);
			FillRect(hDc, &rcWnd, GetSysColorBrush(COLOR_BTNFACE));

			SelectClipRgn(hDc, 0);
			SetMsgHandled(TRUE);
		} else {
			SetMsgHandled(FALSE);
		}
		return TRUE;
	}

	BEGIN_MSG_MAP_EX(CResizablePropertySheetImpl)
		MESSAGE_HANDLER(WM_SHOWWINDOW, OnWmShowWindow) 
		MSG_WM_SIZE(OnSize);
		MSG_WM_ERASEBKGND(OnEraseBackground);
		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_EX(ID_PSHEET_OK, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL_EX(ID_PSHEET_APPLY, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL_EX(ID_PSHEET_CANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL_EX(ID_PSHEET_HELP, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL_EX(ID_PSHEET_TAB, DLSZ_SIZE_X | DLSZ_SIZE_Y)
	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) {
                ::GetWindowRect(GetDlgItem(ID_PSHEET_TOPFRAME), &rctf);
                ::GetWindowRect(GetDlgItem(ID_PSHEET_BOTTOMFRAME), &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);
		}
	}
};

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

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

#if _WIN32_WINNT >= 0x0501
	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;
		}
	}
#endif

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


}; // namespace WTL 

#endif  // __ATL_RESIZABLE_PROPERTY_SHEET_H__ 

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