Environment: MSVC++ 5.00, win95
Many have had a problem with propertysheets embedded in dialog-boxes. The
problem is that the system hangs after the sheet loses the focus and the
window is activated.
Below is some sourcecode for propertysheets and pages:
(this is a part of a big project with dialogeditor, reporteditor)
everything is created on the fly: In my example I have deactivated
the OK and CANCEL (ENTER and ESC-key) for my propertypage-dialogs
(override OnOk and OnCancel).
To use the code all you have to do is the following:
- Create a dialog-project
- Add the two files (CMyPropertySheet.cpp + *.h) to your project
- Create a dialog class (in my example you will find PropPage1 and
PropPage2) - Add the following lines to you OnInitDialog:
// Create the CMyPropertysheet m_propertySheet.m_Rect.left = 100; // set the position on the screen m_propertySheet.m_Rect.top = 100; m_propertySheet.m_Rect.right = 500; m_propertySheet.m_Rect.bottom = 300; m_propertySheet.m_nPages = -1; // init this membervariable m_propertySheet.m_nActPage = 0; // init this membervariable m_propertySheet.m_nCtrlID = 2000; // control-id in the dialog // and create it on the screen m_propertySheet.Create (WS_VISIBLE | WS_CHILD | WS_TABSTOP, m_propertySheet.m_Rect, this, m_propertySheet.m_nCtrlID); // Now add the dialogs page per page TC_ITEM Item; Item.mask = TCIF_TEXT; CMyPropertyPage *pPropPage; int nN; int nPages = 2; // in my example I have two pages for (nN = 0; nN < nPages; nN++) { pPropPage = new (CMyPropertyPage); // new it // Create the tab and the dialog switch (nN) { case 0: // page number 1 - a small example Item.pszText = "Page 1"; pPropPage->m_strTitle = "&Page1"; // or get the title of the dialog // whatwever you want pPropPage->m_pDialogPage = (CMyPropDialog *) &m_propPage1; pPropPage->m_pDialogPage->Create (IDD_PROPPAGE1, &m_propertySheet); break; case 1: // page number 2 - a small example Item.pszText = "Page 2"; pPropPage->m_strTitle = "&Page2"; // or get the title of the dialog // whatwever you want pPropPage->m_pDialogPage = (CMyPropDialog *) &m_propPage2; pPropPage->m_pDialogPage->Create (IDD_PROPPAGE2, &m_propertySheet); break; } m_propertySheet.InsertItem (nN, &Item); // this is for CTabWnd pPropPage->m_hLocal = NULL; // nothing is created on the fly // important information on delete! // add it to the array m_propertySheet.m_Dialogs.Add (pPropPage); m_propertySheet.m_nPages++; // one more page // the size of CTabWnd is m_rect // the size of the dialog is smaller pPropPage->m_Rect.top = 30; // above there must be enough place for the // tab-control pPropPage->m_Rect.left = 10; // border of 10 units is good pPropPage->m_Rect.bottom = m_propertySheet.m_Rect.bottom - m_propertySheet.m_Rect.top - 10; pPropPage->m_Rect.right = m_propertySheet.m_Rect.right - m_propertySheet.m_Rect.left - 10; // Only the 1. page should be active at startup if (nN > 0) { pPropPage->m_pDialogPage->SetWindowPos(NULL, pPropPage->m_Rect.left, pPropPage->m_Rect.top, 0, 0, SWP_HIDEWINDOW | SWP_NOSIZE); } else { pPropPage->m_pDialogPage->SetWindowPos(NULL, pPropPage->m_Rect.left, pPropPage->m_Rect.top, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE); } // the class will handle the change of the tab-control // and synchronize activate-deactivate the dialogpages }
The source code is as follows.
*************** CMyProperty.h *************** #define ID_NEXT_FIELD 1 #define ID_PREV_FIELD 2 //////////////////////////////////////////////////////////////////////////// // Class to handle the pages class CMyPropertyPage : public CObject { public: DLGTEMPLATE m_dlgTemplate; // Templatestructure DLGTEMPLATE *m_pResource; // I create my dialogs on the fly // so I need a pointer to the memory HLOCAL m_hLocal; // Memory-Handle CString m_strTitle; CMyPropDialog *m_pDialogPage; // pointer to the dialog structur CRect m_Rect; // size of the dialog on the screen }; //////////////////////////////////////////////////////////////////////////// // Class to handle the TabCtrl class CMyPropertySheet : public CTabCtrl { // Construction public: CMyPropertySheet(); CRect m_Rect; // Rectangle coordinates UINT m_nCtrlID; // CTrlID in the dialog int m_nPages; // number of pages int m_nActPage; // Actual page // Array of dialogs CObArray m_Dialogs; // Attributes public: // Operations public: int SetActivePage (int nPage); int SetActivePage (CMyPropDialog* pPage); CMyPropDialog *GetPage (int nPage); CMyPropDialog *GetActivePage (void); int GetPageCount (void); BOOL DispPage (int nCommand); // handling pgup and pgdn //{{AFX_VIRTUAL(CMyPropertySheet) public: virtual BOOL PreTranslateMessage(MSG* pMsg); protected: virtual void PostNcDestroy(); //}}AFX_VIRTUAL // Implementation public: virtual ~CMyPropertySheet(); protected: //{{AFX_MSG(CMyPropertySheet) afx_msg void OnSelchange(NMHDR* pNMHDR, LRESULT* pResult); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; //////////////////////////////////////////////////////////////////////////// // CMyPropDialog the dialog class CMyPropDialog : public CDialog { // Konstruction public: CMyPropDialog(CWnd* pParent = NULL); // Standardconstruction // Dialogfields //{{AFX_DATA(CMyPropDialog) // enum { IDD = _UNKNOWN_RESOURCE_ID_ }; I create my dialogs on the fly //}}AFX_DATA //{{AFX_VIRTUAL(CMyPropDialog) public: virtual BOOL PreTranslateMessage(MSG* pMsg); virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo); protected: virtual void DoDataExchange(CDataExchange* pDX); virtual void PostNcDestroy(); //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CMyPropDialog) //}}AFX_MSG DECLARE_MESSAGE_MAP() }; **************** CMyProperty.cpp ******************* //////////////////////////////////////////////////////////////////////////// // CMyPropertySheet CMyPropertySheet::CMyPropertySheet() { } CMyPropertySheet::~CMyPropertySheet() { } BEGIN_MESSAGE_MAP(CMyPropertySheet, CTabCtrl) //{{AFX_MSG_MAP(CMyPropertySheet) ON_NOTIFY_REFLECT(TCN_SELCHANGE, OnSelchange) //}}AFX_MSG_MAP END_MESSAGE_MAP() // Change the tab on the top void CMyPropertySheet::OnSelchange(NMHDR* pNMHDR, LRESULT* pResult) { SetActivePage (GetCurSel ()); *pResult = 0; } //////////////////////////////////////////////////////////////////////////// // This page will be the active page int CMyPropertySheet::SetActivePage (int nPage) { CMyPropertyPage* pPropPage; if (nPage < 0 || nPage > m_nPages) nPage = 0; // get the actual page pPropPage = (CMyPropertyPage *) m_Dialogs.GetAt (m_nActPage); // deactevate it nGLTemp = pPropPage->m_pDialogPage->SetWindowPos (NULL, pPropPage->m_Rect.left, pPropPage->m_Rect.top, 0, 0, SWP_HIDEWINDOW | SWP_NOSIZE); // get the new page pPropPage = (CMyPropertyPage *) m_Dialogs.GetAt (nPage); nGLTemp = pPropPage->m_pDialogPage->SetWindowPos (NULL, pPropPage->m_Rect.left, pPropPage->m_Rect.top, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE); m_nActPage = nPage; // set the CTabCtrl-element SetCurSel(m_nActPage); return TRUE; } //////////////////////////////////////////////////////////////////////////// // Set this page as the active page int CMyPropertySheet::SetActivePage (CMyPropDialog* pPage){ CMyPropertyPage* pPropPage; int nPage, nPageFound=0; for (nPage = 0; nPage <= m_nPages; nPage++) { pPropPage = (CMyPropertyPage *) m_Dialogs.GetAt (nPage); if (pPropPage->m_pDialogPage == pPage) { nPageFound = nPage; break; } } SetActivePage (nPageFound); return TRUE; } //////////////////////////////////////////////////////////////////////////// // Show the next or previous page int CMyPropertySheet::DispPage (int nCommand){ int nPage; nPage = m_nActPage; if (nCommand == ID_NEXT_FIELD) nPage++; else nPage--; // are there correct page numbers? if (nPage < 0) nPage = m_nPages; if (nPage > m_nPages) nPage = 0; SetActivePage (nPage); return TRUE; } //////////////////////////////////////////////////////////////////////////// // Get page number X CMyPropDialog* CMyPropertySheet::GetPage (int nPage){ CMyPropertyPage* pPropPage; if (nPage < 0 || nPage > m_nPages) nPage = 0; pPropPage = (CMyPropertyPage *) m_Dialogs.GetAt (nPage); return pPropPage->m_pDialogPage; } //////////////////////////////////////////////////////////////////////////// // Get the active = current page CMyPropDialog* CMyPropertySheet::GetActivePage (void){ CMyPropertyPage* pPropPage; pPropPage = (CMyPropertyPage *) m_Dialogs.GetAt (m_nActPage); return pPropPage->m_pDialogPage; } //////////////////////////////////////////////////////////////////////////// // Get the number of possible pages int CMyPropertySheet::GetPageCount (void) { return m_nPages; } //////////////////////////////////////////////////////////////////////////// // CMyPropDialog CMyPropDialog::CMyPropDialog(CWnd* pParent /*=NULL*) : CDialog() // : CDialog(CMyPropDialog::IDD, pParent) { //{{AFX_DATA_INIT(CMyPropDialog) // //}}AFX_DATA_INIT } void CMyPropDialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMyPropDialog) // //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CMyPropDialog, CDialog) //{{AFX_MSG_MAP(CMyPropDialog) ON_WM_CTLCOLOR() //}}AFX_MSG_MAP END_MESSAGE_MAP() //////////////////////////////////////////////////////////////////////////// // Messages for CMyPropDialog //////////////////////////////////////////////////////////////////////////// // handle CTRL-PGUP & PGDN BOOL CMyPropDialog::PreTranslateMessage(MSG* pMsg) { // TODO: CWnd *pWnd; /*First Property Sheet tab key translation Ctrl+PageUp, and Ctrl+PageDown * if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && pMsg->wParam == VK_PRIOR) { ((CMyPropertySheet *) GetParent ())->DispPage (ID_PREV_FIELD); return TRUE; } if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && pMsg->wParam == VK_NEXT) { ((CMyPropertySheet *) GetParent ())->DispPage (ID_NEXT_FIELD); return TRUE; } return CDialog::PreTranslateMessage(pMsg); } //////////////////////////////////////////////////////////////////////////// // BOOL CMyPropDialog::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { // TODO: do nothing here return CDialog::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); } //////////////////////////////////////////////////////////////////////////// // CTabCtrl-Handling BOOL CMyPropertySheet::PreTranslateMessage(MSG* pMsg) { // TODO: /*First Property Sheet tab key translation Ctrl+PageUp, and Ctrl+PageDown * if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && pMsg->wParam == VK_PRIOR) { DispPage (ID_PREV_FIELD); return TRUE; } if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && pMsg->wParam == VK_NEXT) { DispPage (ID_NEXT_FIELD); return TRUE; } return CTabCtrl::PreTranslateMessage(pMsg); } void CMyPropertySheet::PostNcDestroy() { // TODO: Free and unlock UINT nN; CMyPropertyPage* pPropPage; // pPropPages deleten for (nN = 0; nN <= m_nPages; nN++) { // get page per page pPropPage = (CMyPropertyPage *) m_Dialogs.GetAt (0); LocalUnlock (pPropPage->m_hLocal); //pBuffer = // (BYTE*)LocalLock(pPropPage->m_hLocal); LocalFree (pPropPage->m_hLocal); // LocalAlloc(LHND, nBufferSize); m_Dialogs.RemoveAt (0); delete (pPropPage); } m_Font.DeleteObject (); CTabCtrl::PostNcDestroy(); delete (this); // free Resources } void CMyPropDialog::PostNcDestroy() { // TODO: CDialog::PostNcDestroy(); delete (this); // free it } //////////////////////////////////////////////////////////////////////////// // Change colors if you want HBRUSH CMyPropDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: Return a different brush if the default is not desired return hbr; }