Link to home
Start Free TrialLog in
Avatar of nchannon
nchannonFlag for Ireland

asked on

MFC Model Dialogs

Hi I am looking for a solution that allows me to post a message back to the parent dialog that was used to open the second popup dialog.
What I mean is in the second dialog has a default button "OK" which has the resource id IDOK I need to send this back to the parent window via a function or other control and not the "OK" button I want to remove this button.
Code from the Parent Dialog which is a button to open the second dialog:
void CMyDlg::OnBnClickedButtonCommandMenu()
{
	if (wEngineer.Create(MAKEINTRESOURCE(CEngineer::IDD), CWnd::FromHandle(NULL)))
	{

		INT_PTR nRet = -1;
		nRet = wEngineer.RunModalLoop(); // keeps the popup hidden
		DWORD dErrp = GetLastError();
		switch (nRet)
		{
		case -1:
			MessageBox(L"Dialog box could not be created!", L"Error", MB_ICONERROR);
			break;
		case IDABORT:
			// Do something
			break;
		case IDOK:
			wEngineer.KillBackGnd();
			::DestroyWindow(wEngineer.GetSafeHwnd());
			theApp.wCOmmandCenter.KillBackGnd();
			::DestroyWindow(theApp.wCOmmandCenter.GetSafeHwnd());
			//PostMessage(WM_COMMAND, IDOK, 0);// Send Message back to shut window 
			break;
		case IDCANCEL:
			// Do something
			break;
		default:
			// Do something
			break;
		};
	}
}

Open in new window


in the second dialog I need to post back to the parent dialogs function that opened this dialog and call the switch id case IDOK: or id case IDCANCEL
This works fine if I click the OK or CANCEL button in the second dialog but I need to be able to send the same command that the ok button sends when clicked but via another control in this case it is a different button with the resource id IDC_BUTTON_ENGINEER

thanks
Avatar of Zoppo
Zoppo
Flag of Germany image

Hi nchannon,

if I understand correctly you have one or more other buttons (in addition to OK and Cancel) and you want to handle each of these buttons press in the parent dialog, right?

If so IMO a solution depends on how you want it to behave: Should the second dialog be closed with those buttons too (as they are with OK or Cancel) or should the second dialog stay opened modal and only a function in the parent dialog should be called?

In the first case you could simply create OnClick handlers for each of these buttons in the second dialog and call 'EndModalLoop' there with the button's ID as parameter, this will then be returned to the calling function as return value of 'RunModalLoop' so you can simply add a 'case' for the button's ID in your 'switch' statement.

If you want to keept the second dialog open you can do different things, i.e. you could send a user-defined message from the second dialog to the parent dialog.

Hope this helps,

ZOPPO
What I understand is that you want to remove the default OK button in your UI which comes by default in MFC dialog and instead add another button on your dialog, or use some other button in your dialog as default button (IDC_BUTTON_ENGINEER), so that it returns ID_OK as the result when clicked on that new button.
Can you try the below code in your second dialog, to this function pass the ID of the button that you want to behave as default button (IDC_BUTTON_ENGINEER may be)?
//call the below function may be like in ONINITDIALOG of second dialog at the end: 
//ChangeDefaultButton(IDC_BUTTON_ENGINEER);
//TODO: Replace CMySecondDialog with name of your second dialog class
int CMySecondDialog::ChangeDefaultButton(const DWORD nNewId) 
{
	DWORD nPrevId;

	nPrevId  = GetDefID();
	// We have to remove the style from old default button
	SendDlgItemMessage(nPrevId, BM_SETSTYLE, BS_PUSHBUTTON, (LPARAM)TRUE);

	SetDefID(nNewId);
	// Add the default style of default button to the new button
	SendDlgItemMessage(nNewId, BM_SETSTYLE, BS_DEFPUSHBUTTON, (LPARAM)TRUE);
	return nPrevId;
}

Open in new window

In the first approach the ID returned would be (IDC_BUTTON_ENGINEER) so you would need a switch case or if/else case in the parent to handle it.
Another approach could be in another button (IDC_BUTTON_ENGINEER), in the click handler, while calling ENDDIALOG, pass the ID as ID_OK. Something like below.
CMySecondDlg::OnAnotherButtonClick()
{
//Do all other work
EndDialog(ID_OK);
}

Open in new window

To demonstrate both the approaches, I have created a demo app for you with comments. Currently first approach is kept ON, you can switch to second approach by following my comments in 2nd dialog.
1> Build and run this app
2> Click on the Launch second dialog button
3> Click on Engineer button
4> Message box shall be popped up with return value.
Download all the files to the same directory and rename Rename DefaultButtonTest.txt to DefaultButtonTest.vcxproj and then open the project, this has been created using visual studio 2010
ChildDialog.h
DefaultButtonTestDlg.cpp
DefaultButtonTestDlg.h
DefaultButtonTest.rc
resource.h
DefaultButtonTest.cpp
DefaultButtonTest.h
ReadMe.txt
stdafx.cpp
stdafx.h
targetver.h
DefaultButtonTest.txt
ChildDialog.cpp
Avatar of nchannon

ASKER

Hi thanks for the reply's,
I have tried all your suggestion's but none of them are returning back to the parent dialog function
//Parent Dialog
void CMyDlg::OnBnClickedButtonCommandMenu()
{
	if (wEngineer.Create(MAKEINTRESOURCE(CEngineer::IDD), CWnd::FromHandle(NULL)))
	{

		INT_PTR nRet = -1;
		nRet = wEngineer.RunModalLoop(); // keeps the popup hidden
		DWORD dErrp = GetLastError();
		switch (nRet)
		{
		case -1:
			MessageBox(L"Dialog box could not be created!", L"Error", MB_ICONERROR);
			break;
		case IDABORT:
			// Do something
			break;
                case IDC_BUTTON_ENGINEER:
                         TRACE(L"Test return value from second dialog");
                          break;
		case IDOK:
			wEngineer.KillBackGnd();
			::DestroyWindow(wEngineer.GetSafeHwnd());
			theApp.wCOmmandCenter.KillBackGnd();
			::DestroyWindow(theApp.wCOmmandCenter.GetSafeHwnd());
			//PostMessage(WM_COMMAND, IDOK, 0);// Send Message back to shut window 
			break;
		case IDCANCEL:
			// Do something
			break;
		default:
			// Do something
			break;
		};
	}
}

Open in new window


maybe I'm doing something wrong I added the function
EndModalLoop(IDOK); and EndModalLoop(IDC_BUTTON_ENGINEER); like this from the second dialog but nothing happened

// second dialog called from, parent dialog
void SecondDlg::OnBnClickedButtonEngineer()
{
  //EndModalLoop(IDOK); 
  EndModalLoop(IDC_BUTTON_ENGINEER); 
}

Open in new window


If its not so simple I might be better to just add additional code with a Sendmessage call back to the parent window and  close the second dialog by adding these two commands in the function but this seems to be double handling
//parent class
LRESULT CMyDlg::KillWindow(WPARAM wparam, LPARAM lparam) {
	theApp.wCOmmandCenter.KillBackGnd();
	::DestroyWindow(theApp.wCOmmandCenter.GetSafeHwnd());
return 0;
}

Open in new window

thanks
Is there any reason you are not launching the second dialog like below:
objSecondDlg.DoModal()?

is the second window a CDialog (class inheriting from CDialog) or a class inheriting from CWnd?
Please see the same project and files that I attached in my above comment, please download and see whether it meets your requirement.
I suggest if you want a Modal dialog,
Call DoModal() method of the second dialog.
to return to the parent dialog simply works by

EndDialog(IDOK);

Open in new window


on success or

EndDialog(IDCANCEL);

Open in new window


you can do that from any handler function. the existence of an IDOK button is not required.

in the parent you called the dialog by

   
int ret = myFirstDlg.DoModal();

Open in new window


after calling EndDialog in myFirstDlg the call would return and the return code exactly is the value the EndDialog function has got for argument.

if the parent dialog wanted to close it own dialog as well it would call EndDialog(ret); and the DoModal of the parent dialog would end as well.

sending messages between dialogs only makes sense for non-modal dialogs or from a modal dialog to a non-modal parent window (for example to a class derived from mfc CView or CFrameWnd).

to close a non-modal dialog you would send WM_CLOSE message.  if you want to close a dialog window like it would be when the user clicked a button you could do like

pSomeWnd->SendMessage(WM_COMMAND, MAKELONG(IDOK, BN_CLICKED), 0);

Open in new window


however, if the window was your own parent waiting for DoModal to return, the statement may lead to a dead-lock.

generally you should try to go without SendMessage or PostMessage between windows of the same gui. instead parent windows should pass the this pointer to child windows and stored as members in the child windows. alternatively GetParent could be used to traverse to the parent and grand parent. for the direction from parent to children, you also have members for controls or sub-windows. so you always could use a direct call rather than sending a message.

Sara
Ok Thanks for the sample code Karrtik Iyer I created a new project and copy your function across to it and it did what I wanted so I implemented it again to my own project and tested it but it didn't work only thing that worked was by clicking the OK button. So I have been working my way though my own code and It seems that what was going on was when I selected the Engineer button which closed  the second dialog then opened a new dialog the message wasn't been returned back to the parent window. So what I have done is in the parent window I have added the case ID IDC_BUTTON_ENGINEER for the engineer button and then called the third dialog from there and all works fine. So I'm assuming but not sure that when the third dialog is created by clicking the Engineer button the focus must be taken away and given to the new dialog.
//code for engineer button in second dialog
void CCommandCenter::OnBnClickedButtonEngineer()
{
	
	SAFE_DELETE(theApp.m_Tip);
	theApp.m_Tip = NULL;
	ChangeDefaultButton(IDC_BUTTON_ENGINEER);//1st Approach: Comment this when you switch 	EndDialog(IDC_BUTTON_ENGINEER);//1st approach:
	
  // create new dialog
	if (theApp.wEngineer.Create(MAKEINTRESOURCE(CEngineer::IDD), CWnd::FromHandle(NULL)))
			{

				INT_PTR nRet = -1;

				nRet = theApp.wEngineer.RunModalLoop();
				DWORD dErrp = GetLastError();
				switch (nRet)
				{
				case -1:
					MessageBox(L"Dialog box could not be created!", L"Error", MB_ICONERROR);
					break;
				case IDABORT:
					// Do something
					break;
				case IDOK:
					//wEngineer.KillBackGnd();
					//::DestroyWindow(wEngineer.GetSafeHwnd());
					theApp.wCOmmandCenter.KillBackGnd();
					::DestroyWindow(theApp.wCOmmandCenter.GetSafeHwnd());
					//PostMessage(WM_COMMAND, IDOK, 0);// Send Message back to shut window 
					break;
				case IDCANCEL:
					// Do something
					break;
				default:
					// Do something
					break;
				};
			}
			break;
		case IDCANCEL:
			// Do something
			break;
		default:
			// Do something
			break;
		};
	}
}

Open in new window


Infact your approach is much better thanks very much as in the second dialog which is infact a dialog holding several buttons in a graphical interface giving a graphic style menu look so each time the menu is selected a drop down menu appears then you select your button and the menu dialog closes then opens which ever dialog you selected, with the menu dialog option always available.
ASKER CERTIFIED SOLUTION
Avatar of Karrtik Iyer
Karrtik Iyer
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
All working perfect thanks again for taking the time to help out