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