locked
Problems with property sheets/property pages RRS feed

  • Question

  • I have some code that creates a property sheet and some property pages:

    class DialogBase
    {
    public:
    	HWND parent;
    	HWND dlg;
    	int dialogid;
    	static std::unordered_map<HWND, DialogBase *> map;
    	DialogBase(HWND Parent, int Id) : parent(Parent), dlg(nullptr), dialogid(Id)
    	{
    	}
    	virtual ~DialogBase()
    	{
    		map[dlg] = nullptr;
    	}
    	int ShowDialog();
    	virtual void OnInitDialog() = 0;
    	virtual void OnOk();
    	virtual void OnCancel();
    	virtual INT_PTR CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
    	static INT_PTR CALLBACK StaticDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
    };
    
    class PropertyPageBase : public DialogBase
    {
    public:
    	PropertyPageBase(int Id);
    	void OnInitDialog();
    	void GetPropSheetPage(PROPSHEETPAGE &page);
    	void SetModified(bool modified);
    	virtual bool OnApply();
    	virtual void OnReset();
    	void OnOk();
    	void OnCancel();
    	INT_PTR CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
    	static INT_PTR CALLBACK StaticPageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
    };
    
    class PropertySheetBase
    {
    public:
    	static PropertySheetBase *_instance;
    	StringClass title;
    	HWND owner;
    	HWND sheet;
    	WNDPROC oldproc;
    	SimpleDynVecClass<PropertyPageBase *> pages;
    	PropertySheetBase(HWND Owner,const char *Title);
    	static int CALLBACK PropSheetCallback(HWND hwndPropSheet, UINT uMsg, LPARAM lParam);
    	static LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    	virtual LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    	void ShowDialog();
    	void Add_Page(PropertyPageBase *base);
    };
    
    std::unordered_map<HWND, DialogBase *> DialogBase::map;
    int DialogBase::ShowDialog()
    {
    	return DialogBoxParam(instance, MAKEINTRESOURCE(dialogid), parent, StaticDlgProc, (LPARAM)this);
    }
    
    void DialogBase::OnOk()
    {
    	EndDialog(dlg, IDOK);
    }
    
    void DialogBase::OnCancel()
    {
    	EndDialog(dlg, IDCANCEL);
    }
    
    INT_PTR CALLBACK DialogBase::DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch (uMsg)
    	{
    	case WM_INITDIALOG:
    		dlg = hwndDlg;
    		OnInitDialog();
    		return TRUE;
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case IDOK:
    			OnOk();
    			return TRUE;
    		case IDCANCEL:
    			OnCancel();
    			return TRUE;
    		}
    		break;
    	}
    	return FALSE;
    }
    
    INT_PTR CALLBACK DialogBase::StaticDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	if (uMsg == WM_INITDIALOG)
    	{
    		map[hwndDlg] = (DialogBase *)lParam;
    	}
    	DialogBase *p = map[hwndDlg];
    	if (p)
    	{
    		return p->DlgProc(hwndDlg, uMsg, wParam, lParam);
    	}
    	return FALSE;
    }
    
    PropertyPageBase::PropertyPageBase(int Id) : DialogBase(nullptr, Id)
    {
    }
    
    void PropertyPageBase::OnInitDialog()
    {
    	parent = GetParent(dlg);
    }
    
    void PropertyPageBase::GetPropSheetPage(PROPSHEETPAGE &page)
    {
    	memset(&page, 0, sizeof(page));
    	page.dwSize = sizeof(PROPSHEETPAGE);
    	page.dwFlags = PSP_PREMATURE;
    	page.hInstance = instance;
    	page.pszTemplate = MAKEINTRESOURCE(dialogid);
    	page.pszIcon = nullptr;
    	page.pfnDlgProc = StaticPageDlgProc;
    	page.lParam = (LPARAM)this;
    }
    
    void PropertyPageBase::SetModified(bool modified)
    {
    	if (modified)
    	{
    		PropSheet_Changed(parent, dlg);
    	}
    	else
    	{
    		PropSheet_UnChanged(parent, dlg);
    	}
    }
    
    bool PropertyPageBase::OnApply()
    {
    	OnOk();
    	return true;
    }
    
    void PropertyPageBase::OnReset()
    {
    	OnCancel();
    }
    
    void PropertyPageBase::OnOk()
    {
    }
    
    void PropertyPageBase::OnCancel()
    {
    }
    
    INT_PTR CALLBACK PropertyPageBase::DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch (uMsg)
    	{
    	case WM_NOTIFY:
    		{
    			LPNMHDR hdr = (LPNMHDR)lParam;
    			switch (hdr->code)
    			{
    			case PSN_APPLY:   //sent when OK or Apply button pressed
    				SetDlgMsgResult(dlg, PSN_APPLY, OnApply() ? PSNRET_NOERROR : PSNRET_INVALID_NOCHANGEPAGE);
    				return TRUE;
    			case PSN_RESET:   //sent when Cancel button pressed
    				OnReset();
    				return TRUE;
    			};
    		}
    		break;
    	}
    	return DialogBase::DlgProc(hwndDlg, uMsg, wParam, lParam);
    }
    
    INT_PTR CALLBACK PropertyPageBase::StaticPageDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	if (uMsg == WM_INITDIALOG)
    	{
    		map[hwndDlg] = (DialogBase *)((PROPSHEETPAGE *)lParam)->lParam;
    	}
    	DialogBase *p = map[hwndDlg];
    	if (p)
    	{
    		return p->DlgProc(hwndDlg, uMsg, wParam, lParam);
    	}
    	return FALSE;
    }
    
    PropertySheetBase *PropertySheetBase::_instance = nullptr;
    PropertySheetBase::PropertySheetBase(HWND Owner, const char *Title) : title(Title), owner(Owner), sheet(nullptr), oldproc(nullptr)
    {
    }
    
    void PropertySheetBase::ShowDialog()
    {
    	PropertySheetBase *oldinstance = _instance;
    	_instance = this;
    	PROPSHEETHEADER psh;
    	memset(&psh, 0, sizeof(psh));
    	PROPSHEETPAGE *psp = new PROPSHEETPAGE[pages.Count()];
    	for (int i = 0; i < pages.Count(); i++)
    	{
    		pages[i]->GetPropSheetPage(psp[i]);
    	}
    	psh.dwSize = sizeof(PROPSHEETHEADER);
    	psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
    	psh.hwndParent = owner;
    	psh.hInstance = instance;
    	psh.pszIcon = nullptr;
    	WideStringClass str = title;
    	psh.pszCaption = str;
    	psh.nPages = pages.Count();
    	psh.ppsp = psp;
    	psh.pfnCallback = PropSheetCallback;
    	PropertySheet(&psh);
    	_instance = oldinstance;
    }
    
    int CALLBACK PropertySheetBase::PropSheetCallback(HWND hwndPropSheet, UINT uMsg, LPARAM lParam)
    {
    	switch (uMsg)
    	{
    	case PSCB_INITIALIZED:
    		{
    			_instance->sheet = hwndPropSheet;
    			_instance->oldproc = SubclassWindow(hwndPropSheet, StaticWindowProc);
    		}
    		break;
    	}
    	return 0;
    }
    
    LRESULT CALLBACK PropertySheetBase::StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	if (_instance)
    	{
    		return _instance->WindowProc(hWnd, uMsg, wParam, lParam);
    	}
    	return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    
    LRESULT CALLBACK PropertySheetBase::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	return CallWindowProc(oldproc, hWnd, uMsg, wParam, lParam);
    }
    
    void PropertySheetBase::Add_Page(PropertyPageBase *base)
    {
    	pages.Add(base);
    }

    I also turn on comctlv6 via this line elsewhere

    #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' " "version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

    I am on Windows 7 and am using the latest version of Visual C++ 2017 (15.9.2).

    I have a derivative of PropertySheetBase and 3 derivatives of PropertyPageBase. In the PropertySheetBase derivative constructor I call Add_Page to add the 3 property pages to my sheet then later I call ShowDialog to actually display the property sheet and its pages.

    However, only the first page is showing up, the other 2 tabs dont appear. Can anyone see anything wrong with my code in terms of how I use property sheets and property pages and anything I should do differently to make the other tabs appear? (it doesn't help that the MSDN documentation for property sheets and pages isn't exactly the best quality and some things are either not documented or not easy to find)

    If you need to see the code that's actually using these classes, I can share the relavent components.

    • Edited by jonwil Tuesday, November 20, 2018 2:40 PM
    Tuesday, November 20, 2018 2:38 PM

Answers

  • As it turns out both the missing tabs contain custom controls and I had forgotten to add the calls that register the window classes for those custom controls.

    • Proposed as answer by Jack Zhang - AAA Wednesday, November 21, 2018 2:17 AM
    • Marked as answer by jonwil Monday, November 26, 2018 10:21 PM
    Tuesday, November 20, 2018 8:34 PM

All replies

  • Have you tried to comment Add_Page calls, keeping only the second or third one?

    Maybe there are some controls that need special initialization.


    Tuesday, November 20, 2018 5:43 PM
  • As it turns out both the missing tabs contain custom controls and I had forgotten to add the calls that register the window classes for those custom controls.

    • Proposed as answer by Jack Zhang - AAA Wednesday, November 21, 2018 2:17 AM
    • Marked as answer by jonwil Monday, November 26, 2018 10:21 PM
    Tuesday, November 20, 2018 8:34 PM
  • Since you have solved the problem, please mark the thread answered.
    Wednesday, November 21, 2018 1:26 AM