// Win32++ Version 7.2 // Released: 5th AUgust 2011 // // David Nash // email: dnash@bigpond.net.au // url: https://sourceforge.net/projects/win32-framework // // // Copyright (c) 2005-2011 David Nash // // Permission is hereby granted, free of charge, to // any person obtaining a copy of this software and // associated documentation files (the "Software"), // to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, // merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom // the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice // shall be included in all copies or substantial portions // of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE // OR OTHER DEALINGS IN THE SOFTWARE. // //////////////////////////////////////////////////////// /////////////////////////////////////////////////////// // propertysheet.h // Declaration of the following classes: // CPropertyPage and CPropertySheet // These classes add support for property sheets to Win32++. A property sheet // will have one or more property pages. These pages are much like dialogs // which are presented within a tabbed dialog or within a wizard. The data // on a property page can be validated before the next page is presented. // Property sheets have three modes of use: Modal, Modeless, and Wizard. // // Refer to the PropertySheet demo program for an example of how propert sheets // can be used. #ifndef _WIN32XX_PROPERTYSHEET_H_ #define _WIN32XX_PROPERTYSHEET_H_ #include "dialog.h" #define ID_APPLY_NOW 0x3021 #define ID_WIZBACK 0x3023 #define ID_WIZNEXT 0x3024 #define ID_WIZFINISH 0x3025 #define ID_HELP 0xE146 #ifndef PROPSHEETHEADER_V1_SIZE #define PROPSHEETHEADER_V1_SIZE 40 #endif namespace Win32xx { class CPropertyPage; typedef Shared_Ptr PropertyPagePtr; class CPropertyPage : public CWnd { public: CPropertyPage (UINT nIDTemplate, LPCTSTR szTitle = NULL); virtual ~CPropertyPage() {} virtual INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); virtual INT_PTR DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); virtual int OnApply(); virtual void OnCancel(); virtual void OnHelp(); virtual BOOL OnInitDialog(); virtual BOOL OnKillActive(); virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); virtual int OnOK(); virtual BOOL OnQueryCancel(); virtual BOOL OnQuerySiblings(WPARAM wParam, LPARAM lParam); virtual int OnSetActive(); virtual int OnWizardBack(); virtual INT_PTR OnWizardFinish(); virtual int OnWizardNext(); virtual BOOL PreTranslateMessage(MSG* pMsg); static UINT CALLBACK StaticPropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp); static INT_PTR CALLBACK StaticDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); void CancelToClose() const; PROPSHEETPAGE GetPSP() const {return m_PSP;} BOOL IsButtonEnabled(int iButton) const; LRESULT QuerySiblings(WPARAM wParam, LPARAM lParam) const; void SetModified(BOOL bChanged) const; void SetTitle(LPCTSTR szTitle); void SetWizardButtons(DWORD dwFlags) const; protected: PROPSHEETPAGE m_PSP; private: CPropertyPage(const CPropertyPage&); // Disable copy construction CPropertyPage& operator = (const CPropertyPage&); // Disable assignment operator tString m_Title; }; class CPropertySheet : public CWnd { public: CPropertySheet(UINT nIDCaption, CWnd* pParent = NULL); CPropertySheet(LPCTSTR pszCaption = NULL, CWnd* pParent = NULL); virtual ~CPropertySheet() {} // Operations virtual CPropertyPage* AddPage(CPropertyPage* pPage); virtual HWND Create(CWnd* pParent = 0); virtual INT_PTR CreatePropertySheet(LPCPROPSHEETHEADER ppsph); virtual void DestroyButton(int iButton); virtual void Destroy(); virtual int DoModal(); virtual void RemovePage(CPropertyPage* pPage); // State functions BOOL IsModeless() const; BOOL IsWizard() const; //Attributes CPropertyPage* GetActivePage() const; int GetPageCount() const; int GetPageIndex(CPropertyPage* pPage) const; HWND GetTabControl() const; virtual BOOL SetActivePage(int nPage); virtual BOOL SetActivePage(CPropertyPage* pPage); virtual void SetIcon(UINT idIcon); virtual void SetTitle(LPCTSTR szTitle); virtual void SetWizardMode(BOOL bWizard); protected: virtual BOOL PreTranslateMessage(MSG* pMsg); virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); private: CPropertySheet(const CPropertySheet&); // Disable copy construction CPropertySheet& operator = (const CPropertySheet&); // Disable assignment operator void BuildPageArray(); static void CALLBACK Callback(HWND hwnd, UINT uMsg, LPARAM lParam); tString m_Title; std::vector m_vPages; // vector of CPropertyPage std::vector m_vPSP; // vector of PROPSHEETPAGE BOOL m_bInitialUpdate; PROPSHEETHEADER m_PSH; }; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// namespace Win32xx { ////////////////////////////////////////// // Definitions for the CPropertyPage class // inline CPropertyPage::CPropertyPage(UINT nIDTemplate, LPCTSTR szTitle /* = NULL*/) { ZeroMemory(&m_PSP, sizeof(PROPSHEETPAGE)); SetTitle(szTitle); m_PSP.dwSize = sizeof(PROPSHEETPAGE); m_PSP.dwFlags |= PSP_USECALLBACK; m_PSP.hInstance = GetApp()->GetResourceHandle(); m_PSP.pszTemplate = MAKEINTRESOURCE(nIDTemplate); m_PSP.pszTitle = m_Title.c_str(); m_PSP.pfnDlgProc = (DLGPROC)CPropertyPage::StaticDialogProc; m_PSP.lParam = (LPARAM)this; m_PSP.pfnCallback = CPropertyPage::StaticPropSheetPageProc; } inline void CPropertyPage::CancelToClose() const // Disables the Cancel button and changes the text of the OK button to "Close." { assert(::IsWindow(m_hWnd)); SendMessage(PSM_CANCELTOCLOSE, 0L, 0L); } inline INT_PTR CPropertyPage::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { // Override this function in your class derrived from CPropertyPage if you wish to handle messages // A typical function might look like this: // switch (uMsg) // { // case MESSAGE1: // Some Win32 API message // OnMessage1(); // A user defined function // break; // Also do default processing // case MESSAGE2: // OnMessage2(); // return x; // Don't do default processing, but instead return // // a value recommended by the Win32 API documentation // } // Always pass unhandled messages on to DialogProcDefault return DialogProcDefault(uMsg, wParam, lParam); } inline INT_PTR CPropertyPage::DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) // All DialogProc functions should pass unhandled messages to this function { LRESULT lr = 0L; switch (uMsg) { case UWM_CLEANUPTEMPS: { TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); pTLSData->vTmpWnds.clear(); } break; case WM_INITDIALOG: return OnInitDialog(); case PSM_QUERYSIBLINGS: return (BOOL)OnQuerySiblings(wParam, lParam); case WM_COMMAND: { // Refelect this message if it's from a control CWnd* pWnd = GetApp()->GetCWndFromMap((HWND)lParam); if (pWnd != NULL) lr = pWnd->OnCommand(wParam, lParam); // Handle user commands if (!lr) lr = OnCommand(wParam, lParam); if (lr) return 0L; } break; case WM_NOTIFY: { // Do Notification reflection if it came from a CWnd object HWND hwndFrom = ((LPNMHDR)lParam)->hwndFrom; CWnd* pWndFrom = GetApp()->GetCWndFromMap(hwndFrom); if (pWndFrom != NULL) lr = pWndFrom->OnNotifyReflect(wParam, lParam); else { // Some controls (eg ListView) have child windows. // Reflect those notifications too. CWnd* pWndFromParent = GetApp()->GetCWndFromMap(::GetParent(hwndFrom)); if (pWndFromParent != NULL) lr = pWndFromParent->OnNotifyReflect(wParam, lParam); } // Handle user notifications if (!lr) lr = OnNotify(wParam, lParam); // Set the return code for notifications if (IsWindow()) SetWindowLongPtr(DWLP_MSGRESULT, (LONG_PTR)lr); return (BOOL)lr; } case WM_PAINT: { if (::GetUpdateRect(m_hWnd, NULL, FALSE)) { CPaintDC dc(this); OnDraw(&dc); } else // RedrawWindow can require repainting without an update rect { CClientDC dc(this); OnDraw(&dc); } break; } case WM_ERASEBKGND: { CDC dc((HDC)wParam); BOOL bResult = OnEraseBkgnd(&dc); dc.Detach(); if (bResult) return TRUE; } break; // A set of messages to be reflected back to the control that generated them case WM_CTLCOLORBTN: case WM_CTLCOLOREDIT: case WM_CTLCOLORDLG: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORSCROLLBAR: case WM_CTLCOLORSTATIC: case WM_DRAWITEM: case WM_MEASUREITEM: case WM_DELETEITEM: case WM_COMPAREITEM: case WM_CHARTOITEM: case WM_VKEYTOITEM: case WM_HSCROLL: case WM_VSCROLL: case WM_PARENTNOTIFY: return MessageReflect(m_hWnd, uMsg, wParam, lParam); } // switch(uMsg) return FALSE; } // INT_PTR CALLBACK CPropertyPage::DialogProc(...) inline BOOL CPropertyPage::IsButtonEnabled(int iButton) const { assert(::IsWindow(m_hWnd)); return GetParent()->GetDlgItem(iButton)->IsWindowEnabled(); } inline int CPropertyPage::OnApply() { // This function is called for each page when the Apply button is pressed // Override this function in your derived class if required. // The possible return values are: // PSNRET_NOERROR. The changes made to this page are valid and have been applied // PSNRET_INVALID. The property sheet will not be destroyed, and focus will be returned to this page. // PSNRET_INVALID_NOCHANGEPAGE. The property sheet will not be destroyed, and focus will be returned; return PSNRET_NOERROR; } inline void CPropertyPage::OnCancel() { // This function is called for each page when the Cancel button is pressed // Override this function in your derived class if required. } inline void CPropertyPage::OnHelp() { // This function is called in response to the PSN_HELP notification. SendMessage(m_hWnd, WM_COMMAND, ID_HELP, 0L); } inline BOOL CPropertyPage::OnQueryCancel() { // Called when the cancel button is pressed, and before the cancel has taken place // Returns TRUE to prevent the cancel operation, or FALSE to allow it. return FALSE; // Allow cancel to proceed } inline BOOL CPropertyPage::OnQuerySiblings(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); // Responds to a query request from the Property Sheet. // The values for wParam and lParam are the ones set by // the CPropertySheet::QuerySiblings call // return FALSE to allow other siblings to be queried, or // return TRUE to stop query at this page. return FALSE; } inline BOOL CPropertyPage::OnInitDialog() { // Called when the property page is created // Override this function in your derived class if required. return TRUE; // Pass Keyboard control to handle in WPARAM } inline BOOL CPropertyPage::OnKillActive() { // This is called in response to a PSN_KILLACTIVE notification, which // is sent whenever the OK or Apply button is pressed. // It provides an opportunity to validate the page contents before it's closed. // Return TRUE to prevent the page from losing the activation, or FALSE to allow it. return FALSE; } inline int CPropertyPage::OnOK() { // Called for each page when the OK button is pressed // Override this function in your derived class if required. // The possible return values are: // PSNRET_NOERROR. The changes made to this page are valid and have been applied // PSNRET_INVALID. The property sheet will not be destroyed, and focus will be returned to this page. // PSNRET_INVALID_NOCHANGEPAGE. The property sheet will not be destroyed, and focus will be returned; return PSNRET_NOERROR; } inline LRESULT CPropertyPage::OnNotify(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); LPPSHNOTIFY pNotify = (LPPSHNOTIFY)lParam; switch(pNotify->hdr.code) { case PSN_SETACTIVE: return OnSetActive(); case PSN_KILLACTIVE: return OnKillActive(); case PSN_APPLY: if (pNotify->lParam) return OnOK(); else return OnApply(); case PSN_RESET: OnCancel(); return FALSE; case PSN_QUERYCANCEL: return OnQueryCancel(); case PSN_WIZNEXT: return OnWizardNext(); case PSN_WIZBACK: return OnWizardBack(); case PSN_WIZFINISH: return OnWizardFinish(); case PSN_HELP: OnHelp(); return TRUE; } return FALSE; } inline int CPropertyPage::OnSetActive() { // Called when a page becomes active // Override this function in your derived class if required. // Returns zero to accept the activation, or -1 to activate the next or the previous page (depending // on whether the user clicked the Next or Back button). To set the activation to a particular page, // return the resource identifier of the page. return 0; } inline int CPropertyPage::OnWizardBack() { // This function is called when the Back button is pressed on a wizard page // Override this function in your derived class if required. // Returns 0 to allow the wizard to go to the previous page. Returns -1 to prevent the wizard // from changing pages. To display a particular page, return its dialog resource identifier. return 0; } inline INT_PTR CPropertyPage::OnWizardFinish() { // This function is called when the Finish button is pressed on a wizard page // Override this function in your derived class if required. // Return Value: // Return non-zero to prevent the wizard from finishing. // Version 5.80. and later. Return a window handle to prevent the wizard from finishing. The wizard will set the focus to that window. The window must be owned by the wizard page. // Return 0 to allow the wizard to finish. return 0; // Allow wizard to finish } inline int CPropertyPage::OnWizardNext() { // This function is called when the Next button is pressed on a wizard page // Override this function in your derived class if required. // Return 0 to allow the wizard to go to the next page. Return -1 to prevent the wizard from // changing pages. To display a particular page, return its dialog resource identifier. return 0; } inline BOOL CPropertyPage::PreTranslateMessage(MSG* pMsg) { // allow the tab control to translate keyboard input if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT)) { CWnd* pWndParent = GetParent(); if (pWndParent->SendMessage(PSM_ISDIALOGMESSAGE, 0L, (LPARAM)pMsg)) return TRUE; } // allow the dialog to translate keyboard input if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST)) { if (IsDialogMessage(pMsg)) return TRUE; } return CWnd::PreTranslateMessage(pMsg); } inline LRESULT CPropertyPage::QuerySiblings(WPARAM wParam, LPARAM lParam) const { // Sent to a property sheet, which then forwards the message to each of its pages. // Set wParam and lParam to values you want passed to the property pages. // Returns the nonzero value from a page in the property sheet, or zero if no page returns a nonzero value. assert(::IsWindow(m_hWnd)); return GetParent()->SendMessage(PSM_QUERYSIBLINGS, wParam, lParam); } inline void CPropertyPage::SetModified(BOOL bChanged) const { // The property sheet will enable the Apply button if bChanged is TRUE. assert(::IsWindow(m_hWnd)); if (bChanged) GetParent()->SendMessage(PSM_CHANGED, (WPARAM)m_hWnd, 0L); else GetParent()->SendMessage(PSM_UNCHANGED, (WPARAM)m_hWnd, 0L); } inline void CPropertyPage::SetTitle(LPCTSTR szTitle) { if (szTitle) { m_Title = szTitle; m_PSP.dwFlags |= PSP_USETITLE; } else { m_Title.erase(); m_PSP.dwFlags &= ~PSP_USETITLE; } m_PSP.pszTitle = m_Title.c_str(); } inline void CPropertyPage::SetWizardButtons(DWORD dwFlags) const { // dwFlags: A value that specifies which wizard buttons are enabled. You can combine one or more of the following flags. // PSWIZB_BACK Enable the Back button. If this flag is not set, the Back button is displayed as disabled. // PSWIZB_DISABLEDFINISH Display a disabled Finish button. // PSWIZB_FINISH Display an enabled Finish button. // PSWIZB_NEXT Enable the Next button. If this flag is not set, the Next button is displayed as disabled. assert (::IsWindow(m_hWnd)); PropSheet_SetWizButtons(::GetParent(m_hWnd), dwFlags); } inline UINT CALLBACK CPropertyPage::StaticPropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp) { assert( GetApp() ); UNREFERENCED_PARAMETER(hwnd); // Note: the hwnd is always NULL switch (uMsg) { case PSPCB_CREATE: { TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); assert(pTLSData); // Store the CPropertyPage pointer in Thread Local Storage pTLSData->pCWnd = (CWnd*)ppsp->lParam; } break; } return TRUE; } inline INT_PTR CALLBACK CPropertyPage::StaticDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { assert( GetApp() ); // Find matching CWnd pointer for this HWND CPropertyPage* pPage = (CPropertyPage*)GetApp()->GetCWndFromMap(hwndDlg); if (0 == pPage) { // matching CWnd pointer not found, so add it to HWNDMap now TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); pPage = (CPropertyPage*)pTLSData->pCWnd; // Set the hWnd members and call DialogProc for this message pPage->m_hWnd = hwndDlg; pPage->AddToMap(); } return pPage->DialogProc(uMsg, wParam, lParam); } /////////////////////////////////////////// // Definitions for the CPropertySheet class // inline CPropertySheet::CPropertySheet(UINT nIDCaption, CWnd* pParent /* = NULL*/) { ZeroMemory(&m_PSH, sizeof (PROPSHEETHEADER)); SetTitle(LoadString(nIDCaption)); m_bInitialUpdate = FALSE; #ifdef _WIN32_WCE m_PSH.dwSize = sizeof(PROPSHEETHEADER); #else if (GetComCtlVersion() >= 471) m_PSH.dwSize = sizeof(PROPSHEETHEADER); else m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE; #endif m_PSH.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK; m_PSH.hwndParent = pParent? pParent->GetHwnd() : 0; m_PSH.hInstance = GetApp()->GetInstanceHandle(); m_PSH.pfnCallback = (PFNPROPSHEETCALLBACK)CPropertySheet::Callback; } inline CPropertySheet::CPropertySheet(LPCTSTR pszCaption /*= NULL*/, CWnd* pParent /* = NULL*/) { ZeroMemory(&m_PSH, sizeof (PROPSHEETHEADER)); SetTitle(pszCaption); m_bInitialUpdate = FALSE; #ifdef _WIN32_WCE m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE; #else if (GetComCtlVersion() >= 471) m_PSH.dwSize = sizeof(PROPSHEETHEADER); else m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE; #endif m_PSH.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK; m_PSH.hwndParent = pParent? pParent->GetHwnd() : 0;; m_PSH.hInstance = GetApp()->GetInstanceHandle(); m_PSH.pfnCallback = (PFNPROPSHEETCALLBACK)CPropertySheet::Callback; } inline CPropertyPage* CPropertySheet::AddPage(CPropertyPage* pPage) // Adds a Property Page to the Property Sheet { assert(NULL != pPage); m_vPages.push_back(PropertyPagePtr(pPage)); if (m_hWnd) { // property sheet already exists, so add page to it PROPSHEETPAGE psp = pPage->GetPSP(); HPROPSHEETPAGE hpsp = ::CreatePropertySheetPage(&psp); PropSheet_AddPage(m_hWnd, hpsp); } m_PSH.nPages = (int)m_vPages.size(); return pPage; } inline void CPropertySheet::BuildPageArray() // Builds the PROPSHEETPAGE array { m_vPSP.clear(); std::vector::iterator iter; for (iter = m_vPages.begin(); iter < m_vPages.end(); ++iter) m_vPSP.push_back((*iter)->GetPSP()); PROPSHEETPAGE* pPSPArray = &m_vPSP.front(); // Array of PROPSHEETPAGE m_PSH.ppsp = pPSPArray; } inline void CALLBACK CPropertySheet::Callback(HWND hwnd, UINT uMsg, LPARAM lParam) { assert( GetApp() ); switch(uMsg) { //called before the dialog is created, hwnd = NULL, lParam points to dialog resource case PSCB_PRECREATE: { LPDLGTEMPLATE lpTemplate = (LPDLGTEMPLATE)lParam; if(!(lpTemplate->style & WS_SYSMENU)) { lpTemplate->style |= WS_SYSMENU; } } break; //called after the dialog is created case PSCB_INITIALIZED: { // Retrieve pointer to CWnd object from Thread Local Storage TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); assert(pTLSData); CPropertySheet* w = (CPropertySheet*)pTLSData->pCWnd; assert(w); w->Attach(hwnd); w->OnCreate(); } break; } } inline HWND CPropertySheet::Create(CWnd* pParent /*= 0*/) // Creates a modeless Property Sheet { assert( GetApp() ); if (pParent) { m_PSH.hwndParent = pParent->GetHwnd(); } BuildPageArray(); PROPSHEETPAGE* pPSPArray = &m_vPSP.front(); m_PSH.ppsp = pPSPArray; // Create a modeless Property Sheet m_PSH.dwFlags &= ~PSH_WIZARD; m_PSH.dwFlags |= PSH_MODELESS; HWND hWnd = (HWND)CreatePropertySheet(&m_PSH); return hWnd; } inline INT_PTR CPropertySheet::CreatePropertySheet(LPCPROPSHEETHEADER ppsph) { assert( GetApp() ); INT_PTR ipResult = 0; // Only one window per CWnd instance allowed assert(!::IsWindow(m_hWnd)); // Ensure this thread has the TLS index set TLSData* pTLSData = GetApp()->SetTlsIndex(); // Store the 'this' pointer in Thread Local Storage pTLSData->pCWnd = this; // Create the property sheet ipResult = PropertySheet(ppsph); return ipResult; } inline void CPropertySheet::DestroyButton(int IDButton) { assert(::IsWindow(m_hWnd)); HWND hwndButton = ::GetDlgItem(m_hWnd, IDButton); if (hwndButton != NULL) { // Hide and disable the button ::ShowWindow(hwndButton, SW_HIDE); ::EnableWindow(hwndButton, FALSE); } } inline void CPropertySheet::Destroy() { CWnd::Destroy(); m_vPages.clear(); } inline int CPropertySheet::DoModal() { assert( GetApp() ); BuildPageArray(); PROPSHEETPAGE* pPSPArray = &m_vPSP.front(); m_PSH.ppsp = pPSPArray; // Create the Property Sheet int nResult = (int)CreatePropertySheet(&m_PSH); m_vPages.clear(); return nResult; } inline CPropertyPage* CPropertySheet::GetActivePage() const { assert(::IsWindow(m_hWnd)); CPropertyPage* pPage = NULL; if (m_hWnd != NULL) { HWND hPage = (HWND)SendMessage(PSM_GETCURRENTPAGEHWND, 0L, 0L); pPage = (CPropertyPage*)FromHandle(hPage); } return pPage; } inline int CPropertySheet::GetPageCount() const // Returns the number of Property Pages in this Property Sheet { assert(::IsWindow(m_hWnd)); return (int)m_vPages.size(); } inline int CPropertySheet::GetPageIndex(CPropertyPage* pPage) const { assert(::IsWindow(m_hWnd)); for (int i = 0; i < GetPageCount(); i++) { if (m_vPages[i].get() == pPage) return i; } return -1; } inline HWND CPropertySheet::GetTabControl() const // Returns the handle to the Property Sheet's tab control { assert(::IsWindow(m_hWnd)); return (HWND)SendMessage(PSM_GETTABCONTROL, 0L, 0L); } inline BOOL CPropertySheet::IsModeless() const { return (m_PSH.dwFlags & PSH_MODELESS); } inline BOOL CPropertySheet::IsWizard() const { return (m_PSH.dwFlags & PSH_WIZARD); } inline void CPropertySheet::RemovePage(CPropertyPage* pPage) // Removes a Property Page from the Property Sheet { assert(::IsWindow(m_hWnd)); int nPage = GetPageIndex(pPage); if (m_hWnd != NULL) SendMessage(m_hWnd, PSM_REMOVEPAGE, nPage, 0L); m_vPages.erase(m_vPages.begin() + nPage, m_vPages.begin() + nPage+1); m_PSH.nPages = (int)m_vPages.size(); } inline BOOL CPropertySheet::PreTranslateMessage(MSG* pMsg) { // allow sheet to translate Ctrl+Tab, Shift+Ctrl+Tab, Ctrl+PageUp, and Ctrl+PageDown if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT)) { if (SendMessage(PSM_ISDIALOGMESSAGE, 0L, (LPARAM)pMsg)) return TRUE; } // allow the dialog to translate keyboard input if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST)) { return GetActivePage()->PreTranslateMessage(pMsg); } return CWnd::PreTranslateMessage(pMsg); } inline BOOL CPropertySheet::SetActivePage(int nPage) { assert(::IsWindow(m_hWnd)); return (BOOL)SendMessage(m_hWnd, PSM_SETCURSEL, nPage, 0L); } inline BOOL CPropertySheet::SetActivePage(CPropertyPage* pPage) { assert(::IsWindow(m_hWnd)); int nPage = GetPageIndex(pPage); if ((nPage >= 0)) return SetActivePage(nPage); return FALSE; } inline void CPropertySheet::SetIcon(UINT idIcon) { m_PSH.pszIcon = MAKEINTRESOURCE(idIcon); m_PSH.dwFlags |= PSH_USEICONID; } inline void CPropertySheet::SetTitle(LPCTSTR szTitle) { if (szTitle) m_Title = szTitle; else m_Title.erase(); m_PSH.pszCaption = m_Title.c_str(); } inline void CPropertySheet::SetWizardMode(BOOL bWizard) { if (bWizard) m_PSH.dwFlags |= PSH_WIZARD; else m_PSH.dwFlags &= ~PSH_WIZARD; } inline LRESULT CPropertySheet::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_WINDOWPOSCHANGED: { LPWINDOWPOS lpWinPos = (LPWINDOWPOS)lParam; if (lpWinPos->flags & SWP_SHOWWINDOW) { if (!m_bInitialUpdate) // The first window positioning with the window visible OnInitialUpdate(); m_bInitialUpdate = TRUE; } } break; case WM_DESTROY: m_bInitialUpdate = FALSE; break; case WM_SYSCOMMAND: if ((SC_CLOSE == wParam) && (m_PSH.dwFlags & PSH_MODELESS)) { Destroy(); return 0L; } break; } // pass unhandled messages on for default processing return CWnd::WndProcDefault(uMsg, wParam, lParam); } } #endif // _WIN32XX_PROPERTYSHEET_H_