How to OnInitDialog every CPropertyPage in MFC? - c++

I have 2 CPropertyPage objects; right now, the second page will only hit OnInitDialog if I click on the second page.
How can I initialize it right away when program starts?

You can add PSP_PREMATURE to the dwFlags field of each property page's m_psp data member (a PROPSHEETPAGE structure). This forces the actual creation of that page (thus calling its OnInitDialog() function) when the parent property sheet is created, rather than waiting until the page is selected.
The only online documentation I can find for this is now 'deprecated', but the technique does still work.
dwFlags ... PSP_PREMATURE The page is created when the property sheet is created. Usually, the page is not created until the
first time it is selected.
You can add this flag in the constructor for each page; something like this:
class MyPropPage : public CPropertyPage
{
public:
MyPropPage(UINT idd) : CPropertyPage(idd) {
m_psp.dwFlags |= PSP_PREMATURE; // Add the 'premature' flag on construction
//...
}
//...
};

You might want to use a WM_TIMER message in your CPropertySheet like this:
BOOL CMyPropertySheet::OnInitDialog()
{
SetTimer(1, 1, NULL);
return CPropertySheet::OnInitDialog();
}
void CMyPropertySheet::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == 1)
{
KillTimer(1);
SetActivePage(1); // initialize second tab
SetTimer(2, 1, NULL);
}
else if (nIDEvent == 2)
{
KillTimer(2);
SetActivePage(0); // back to first tab
// to hide the initialization process, you might want to create
// CMyPropertySheet with the WS_VISIBLE style disabled and wait
// until all pages are initialized:
ShowWindow(SW_SHOW);
}
CPropertySheet::OnTimer(nIDEvent);
}

Related

How to activate a button (CButton) located in a disabled window (CWnd)?

I have this code :
m_pBtnCom = new CButton();
m_pBtnCom->Create(_T("Push"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_TEXT|BS_VCENTER|BS_CENTER, rc, this, BTN_CMT);
Where:
this = my derived CWnd class
rc = CRect button position
BTN_CMT = button id
Current context:
If I disable the parent CWnd by calling EnableWindow(FALSE), even if I call the function EnableWindow(TRUE) on the button (m_pBtnCom->EnableWindow(TRUE)), the latter remains disabled; Therefore, nothing works on it: click, tooltip, ...
I tried to remove WS_CHILD, without success
Question:
Is it possible to activate the button when the window (argument this in my code) is disabled?
Child window can't be independently enabled when parent window is disabled. You can instead enable all children, then go back and enable the particular button.
Note, if you have IDCANCEL button, and you disable it, then the dialog's close button is not functioning either and it gets confusing. You may want to avoid disabling the cancel button and override OnCancel
void CMyDialog::enable_children(bool enable)
{
auto wnd = GetWindow(GW_CHILD);
while (wnd)
{
wnd->EnableWindow(enable);
wnd = wnd->GetWindow(GW_HWNDNEXT);
}
}
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
enable_children(FALSE);
//re-enable one button
if(GetDlgItem(IDCANCEL)) GetDlgItem(IDCANCEL)->EnableWindow(TRUE);
return TRUE;
}
void OnCancel()
{
MessageBox(L"cancel...");
CDialog::OnCancel();
}

MFC: How do you implement a context menu for CRichEditView in a CTabView tab?

I have a CTabView with one of the tabs a CRichEditView. Rich text is added to the control and shows fine. If I select text within the CRichEditView the toolbar edit items work fine (for example, copy highlights, and if I click on it, it copies to the clipboard). However, I found that if I selected text and right click there is no context menu with a CRichEditView like there was with CEditView. Searching the Internet, I found an implementation for CRichEditView::GetContextMenu() to try and use. It first had an assert failure because the CDocument is not a rich text type, so for testing, I removed it (commented out below) and ended up with the following:
HMENU CMyRichView::GetContextMenu(WORD seltyp, LPOLEOBJECT lpoleobj, CHARRANGE* lpchrg)
{
// TODO: Add your specialized code here and/or call the base class
/*
CRichEditCntrItem* pItem = GetSelectedItem();
if (pItem == NULL || !pItem->IsInPlaceActive())*/
{
CMenu menuText;
menuText.LoadMenu(IDR_CONTEXT_EDIT_MENU);
CMenu* pMenuPopup = menuText.GetSubMenu(0);
menuText.RemoveMenu(0, MF_BYPOSITION);
return pMenuPopup->Detach();
}
}
Where the IDR_CONTEXT_EDIT_MENU is:
IDR_CONTEXT_EDIT_MENU MENU
BEGIN
POPUP "edit"
BEGIN
MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY
END
END
Now when I right-click I see the context menu. However, when I choose "copy", nothing happens. So I mapped the ID_EDIT_COPY so I could set a break point to see if it was called.
void CMyRichView::OnEditCopy()
{
// TODO: Add your command handler code here
ASSERT_VALID(this);
GetRichEditCtrl().Copy();
}
It's not if the context item is used, but it is if the toolbar is used.
What am I missing and doing wrong?
TIA!!
If the message goes to CTabView then add CTabView::OnEditCopy handler.
Otherwise, you can override PreTranslateMessage as shown below, this will make sure the message is sent to CMyRichEditView::OnEditCopy.
BOOL CMyRichEditView::PreTranslateMessage(MSG *msg)
{
if(msg->message == WM_CONTEXTMENU || msg->message == WM_RBUTTONDOWN)
{
CMenu menu;
menu.LoadMenu(IDR_CONTEXT_EDIT_MENU);
int c = menu.GetMenuItemCount();
CMenu* popup = menu.GetSubMenu(0);
popup->TrackPopupMenu(0, msg->pt.x, msg->pt.y, this, NULL);
return TRUE;
}
return CRichEditView::PreTranslateMessage(msg);
}

How to display values in child MFC dialog box based on variables set in parent dialog box?

I have a MFC project I am working on. In the main dialog box there is a button to open a child dialog box for some user input. This data is then set to variables in the parent dialog box when OK is clicked in the child box. This bit all works perfectly fine.
Right now the text boxes in the child box initialize to preset values in the OnInitDialog() of the child dialog box. I would like these values to initialize to whatever the variable they are connected to in the parent dialog box currently is.
So, for example I have a text box that sets in integer variable called sampleCount. In the child dialog box I have (just showing the code for this variable)
void ChildBox::DoDataExchange(CdataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_SAMPCOUNT, sampCnt);
}
BOOL FNameIn::OnInitDialog()
{
CDialog::OnInitDialog();
GetDlgItem(IDC_SAMPCOUNT)->SetWindowTextA("1");
return TRUE;
}
Then in the parent dialog box I have
void ParentDialog::OnInput()
{
ChildBox dlg;
if (dlg.DoModal() == IDOK)
{
sampleCount = dlg.sampCnt;
}
}
As I said, this code all works, but every time I open the ChildBox the sampleCount text box is set to 1. If I type in 20, then hit ok and set the sampleCount variable in ParentDialog to 20 I want it to show a 20 in the text box next time I open the child dialog box. The variable could also be set automatically by another function in ParentDialog, so the last value entered in child dialog isn't always correct, it needs to use whatever is currently the value of the variable sampleCount in ParentDialog.
I feel like this should be pretty straight forward but I can't quite figure it out, thanks for the help.
Modify the child dialog's constructor to accept a CString parameter and store that into the child's sampCnt variable. Then the MFC code will display it in the control because of the DDX_Text statement.
Remove the GetDlgItem(IDC_SAMPCOUNT)->SetWindowTextA("1"); line and set the value before calling dlg.DoModal(), like
ChildBox dlg;
dlg.sampCnt = sampleCount;
if (dlg.DoModal() == IDOK)
{ sampleCount = dlg.sampCnt;
}
I think you are creating local variable of ChildBox Dialog
so , even if you are assigning a value to its variable it will not work.
Rather create a pointer variable of ChildBox Dialog
void ParentDialog::OnInput()
{
ChildBox *dlg;
if (dlg->DoModal() == IDOK)
{
sampleCount = dlg->sampCnt;
}
dlg = NULL ;
}

MFC, EndDialog, repeated dialog creation causes unexpected behavior

I need to adjust the dialog window dynamically based on its size. To do so I employ the following technique:
I load it up and get its size from the CDialog::OnInitDialog() handler.
If the size is too big, I end the dialog by calling CDialog::EndDialog
And then update global variable and reinit the dialog-derived class again with the size adjustment.
What happens is that on the second pass, some APIs start acting strangely. For instance, MessageBox does not show (thus all ASSERT macros stop working) and some SetWindowText APIs crash the app. Any idea why?
Here're the code snippets:
#define SPECIAL_VALUE -1
//From CWinApp-derived class
BOOL CWinAppDerivedClass::InitInstance()
{
//...
for(;;)
{
CDialogDerivedClass dlg(&nGlobalCounter);
m_pMainWnd = &dlg;
if(dlg.DoModal() != SPECIAL_VALUE)
break;
}
//...
}
And then from the dialog class itself:
//From CDialogDerivedClass
BOOL CDialogDerivedClass::OnInitDialog()
{
//The following API shows message box only on the 1st pass, why?
::MessageBox(NULL, L"1", L"2", MB_OK);
//...
if(checkedDialogSizeIndicatesReload)
{
this->EndDialog(SPECIAL_VALUE);
return FALSE;
}
//Continue loading dialog as usual
...
}
EDIT: I noticed by chance that if I comment out the following line it seems to work. Any idea why?
//m_pMainWnd = &dlg;
Variable dlg is not yet a window at the place where you are setting m_pMainWnd (the dialog box is displayed only after OnInitInstance returns TRUE); the Following code should work:
for(;;)
{
CDialogDerivedClass dlg(&nGlobalCounter);
// m_pMainWnd = &dlg;
if(dlg.DoModal() != SPECIAL_VALUE)
break;
}
m_pMainWnd = &dlg;
InitDialog is the last message processed before the dialog window appears on the screen - you can detect and adjust the size in place and not have the kind of funky global variable thing you are doing.
if(checkedDialogSizeIndicatesReload)
{
// look up SetWindowPos -
// I am nt sure if there is another parameter or not that is optional
int x,y,cx,cy;
WINDOWPLACEMENT wp;
GetWindowPlacement(&wp);
// calc new size here
SetWindowPos(this,x,y,cx,cy);
}
// window appears when the message handler returns

CMFCCaptionMenuButton alternative?

I need to create a caption bar button for a CDockablePane which will call up a menu with various options. I tried to use CMFCCaptionMenuButton and the button and menu show up but the message map methods for the menu ids don't fire. The MFC documentation states that CMFCCaptionMenuButton is meant for internal infrastructure and not really for your code.
So assuming that is what my problem is should I be using a CMFCCaptionBarButton and then making a separate popup menu? Has anyone made a similar caption bar based menu in MFC before?
Here's some slimmed down code snippets in case I just made a stupid mistake in hooking up the events:
BEGIN_MESSAGE_MAP(CDockPane, CDockablePane)
ON_COMMAND(ID_MORPH_BROWSER, OnMorphBrowser)
END_MESSAGE_MAP()
void CDockPane::OnPressButtons(UINT nHit)
{
// only for custom button handling don't call base
// close, maximize, and pin will be handled by default
switch (nHit)
{
case ID_MORPHTEST:
{
CMorphMenuButton* pButton = dynamic_cast<CMorphMenuButton*>(m_arrButtons.GetAt(m_morphIndex));
pButton->ShowMenu(this);
break;
}
}
}
void CDockPane::SetCaptionButtons()
{
CDockablePane::SetCaptionButtons(); // for close, pin etc
m_morphIndex = m_arrButtons.Add(new CMorphMenuButton(ID_MORPHTEST));
}
void CDockPane::OnMorphBrowser()
{
// do stuff on menu item click
}
Edit: Removed previous code no longer in use
Now that the sound of crickets chirping has dwindled in the background I guess I'll post the workaround I currently have in place:
Instead of inheriting and extending CMFCCaptionMenuButton I build my class by extending CMFCCaptionButton. I then create a menu and provide a ShowMenu method to be explicitly called when handling the custom button events as well as overriding GetIconID to return a particular system icon for the button for each menu added to the caption bar ending up with something like this for the example outlined in the question:
#pragma once
// CMorphMenuButton command target
class CMorphMenuButton : public CMFCCaptionButton
{
public:
CMorphMenuButton(UINT nHit);
virtual ~CMorphMenuButton();
virtual CMenuImages::IMAGES_IDS GetIconID (BOOL bHorz, BOOL bMaximized) const;
void ShowMenu(CWnd* pWnd);
private:
CMenu m_dockMenu;
CMenu* m_subMenu;
};
// MorphMenuButton.cpp : implementation file
//
#include "stdafx.h"
#include "MorphMenuButton.h"
// CMorphMenuButton
CMorphMenuButton::CMorphMenuButton(UINT nHit)
: CMFCCaptionButton(nHit)
{
SetMiniFrameButton(); // already defaulted?
m_dockMenu.LoadMenu(IDR_DOCKPANE); // resource ID for dock pane menus
}
CMorphMenuButton::~CMorphMenuButton()
{
m_dockMenu.DestroyMenu();
}
CMenuImages::IMAGES_IDS CMorphMenuButton::GetIconID(BOOL bHorz, BOOL bMaximized) const
{
return CMenuImages::IdArrowForward;
}
void CMorphMenuButton::ShowMenu(CWnd* pWnd)
{
CRect windowRect, buttonRect;
pWnd->GetWindowRect(&windowRect);
buttonRect = GetRect();
CPoint menuPos(windowRect.left + buttonRect.right, windowRect.top + buttonRect.bottom);
m_subMenu = m_dockMenu.GetSubMenu(0);
if (!m_subMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, menuPos.x, menuPos.y, pWnd))
{
DWORD id = GetLastError();
wchar_t errMsg[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, id, 0, errMsg, sizeof(errMsg), 0);
MessageBox(0, errMsg, L"Error", MB_OK);
}
}
The setting of caption bar buttons and handling of click events for both buttons and menus are the same as defined in the question and this works.