Modeless Dialogs


Click here to change the theme.

There are a few details about using MFC to create and use modeless dialogs that are documented in various places. So I hope it is helpful to have this summary to use instead of all the many references in the MFC documentation.

One thing that it helps to understand is that dialogs can be created either on the stack or in the heap. This is true of course for most any object, but the details of how to program a modeless dialog is more dependent than other objects upon whether it exists on the stack or in the heap. I will first show how to make a modeless-dialog-based application that creates the modeless dialog on the heap.

Thanks to CodeGuru DanielK, I have been informed of a new Knowledge Base article describing how to create modeless dialogs. I wish the documentation existed when I first struggled with creating modeless dialogs. See: Q103788 - INFO: Creating a Modeless Dialog Box with MFC Libraries.

If your project is an ATL .exe project, then see Q216503 - PRB: Problems Showing an ATL Dialog Box as Modeless in ATL .exe.

You can create a modeless-dialog-based application by generating a dialog-based application using AppWizard, then modifying it as follows. Use ClassWizard to override PostNcDestroy, OnOK and OnCancel in the dialog and make them look something like this:

void CModelessDlg::PostNcDestroy() {
CDialog::PostNcDestroy();
delete this;
}
void CModelessDlg::OnOK() {
MessageBox("Ok");
}
void CModelessDlg::OnCancel() {
DestroyWindow();
}

The override for PostNcDestroy is necessary because our program does not know when the dialog will be dismissed. So the dialog must take care of removing itself from the system. Refer to the documentation for CDialog::OnOK and CDialog::OnCancel for an explanation of those overrides. In particular, notice that if you want OnOK to dismiss the dialog, then you need to call DestroyWindow in OnOK also.

Then in your application's InitInstance, replace:

CModelessDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
	{
	}
else if (nResponse == IDCANCEL)
	{
	}

with:

CModelessDlg *pModelessDlg = new CModelessDlg();
m_pMainWnd = pModelessDlg;
if (pModelessDlg->Create(IDD_MODELESS_DIALOG))
	return TRUE;

To understand what is happening here, it is necessary to understand a couple of things about CWinApp::InitInstance. InitInstance normally executes, then exits after it has created the main window. In a dialog-based application, though, InitInstance is usually used to execute the entire application, in which case the application exits after the dialog exits. For a modeless-dialog-based application, though, we want to use InitInstance the way it is normally used; that is, we want to create the modeless dialog in InitInstance but then we want the dialog to be able to persist after InitInstance has finished. So in the original dialog-based application, the modal dialog is created on the stack and is therefore deleted when InitInstance exits, but we modify that by creating the dialog in the heap.


Note that in the CDialog documentation it says "If you wish to create a modeless dialog, call Create in the constructor of your dialog class.". I do not understand that and it seems to be unnecessary. In CDialog::Create it says "You can put the call to Create inside the constructor or call it after the constructor is invoked.".

Also, OnOK says to call DestroyWindow in OnOk. This should be done if you want the dialog to end when the Ok button is pressed, but it is not necessary if you want the dialog to continue after the Ok button is pressed.

I usually rename the "Cancel" button so that it's name is "Exit", since that makes more sense. The command id (ID_CANCEL) could still be used, though.

Creating a Modeless Dialog On the Stack

It is possible to create a modeless dialog on the stack, and there are situations when this would be the best solution. A good example of when to create a modeless dialog on the stack is for a progress dialog that is used in just one function and then is not needed after the function completes. If a modeless dialog is created on the stack, then the override for PostNcDestroy is not needed and should not be used (at least not the "delete this"), since the dialog will be deleted automatically when the function exits.


Your results can vary dependent upon the version of VC you have.