4
votes

I have followed this question to make a non-modal/modeless dialog: How to display a non-modal CDialog?

I'm using MFC/C++ in VS2008. I'm more fluent with C# and .net than with MFC and C++.

I have a menu item in my form that launches the dialog. There can only be one instance of the dialog opened. The dialog displays fine. I can close it by clicking the X in the corner and it closes when I close the main form. The problem I am having is the dialog cannot be opened again after I click the X to close the dialog. I know it is because the pointer is never set back to NULL.

I have this in my form's header file:

CChildDialog *m_pDialog;

I have this part in my form's constructor:

m_pDialog = NULL;

When clicking on a menu item I have this code in the menu item's method (I modified it from the other SO answer because I only want one instance of the dialog opened):

if(m_pDialog == NULL)
{
    // Invoking the Dialog
    m_pDialog = new CChildDialog();
    BOOL ret = m_pDialog->Create(IDD_CHILDDIALOG, this);

    if (!ret)   //Create failed.
    {
        AfxMessageBox(_T("Error creating Dialog"));
    }    

    m_pDialog->ShowWindow(SW_SHOW);
}

Now I know I need to execute this part and set the pointer to NULL, but I don't know where to put this:

// Delete the dialog once done
delete m_pDialog;
m_pDialog = NULL;

Do I need to keep monitoring if the dialog has been disposed? Is there an event triggered to the parent form when the dialog is closed?

4
Just add a message handler for WM_DESTROY that resets the pointer.Hans Passant

4 Answers

4
votes

If you want to recycle the contents of the window after closing it with X, you have to handle the WM_CLOSE message in your dialog:

void CChildDialog::OnClose()
{
    ShowWindow(SW_HIDE);
}

Then in the code that opens the window:

if(m_pDialog == NULL)
{
    // Invoking the Dialog
    m_pDialog = new CChildDialog();
    BOOL ret = m_pDialog->Create(IDD_CHILDDIALOG, this);

    if (!ret)   //Create failed.
    {
        AfxMessageBox(_T("Error creating Dialog"));
    }    
}

m_pDialog->ShowWindow(SW_SHOW); //moved outside the if(m_pDialog == NULL)

Hope it can help

3
votes

If you want to delete the modeless dialog, then just do so.

If you want to delete the dialog's object when the user closed the modeless dialog you might take a look at WM_PARENTNOTIFY. If a child window is destroyed and the child windows has not the extended window style WS_EX_NOPARENTNOTIFY set, then windows sends a WM_PARENTNOTIFY with wParam=WM_DESTROY to the parent window. You should implement a handler for that message in the parent window and check if it's the modeless dialog that is being destroyed.

1
votes

I had the question drafted up and was ready to post it, but then I had an idea and ended up solving my own problem. So for anyone else who has an issue with detecting the closing of a modeless dialog, this is what I did:

void Form1::MenuItemMethod()
{
    if(m_pDialog == NULL)
    {
        // Invoking the Dialog
        m_pDialog = new CChildDialog();
        BOOL ret = m_pDialog->Create(IDD_CHILDDIALOG, this);

        if (!ret)   //Create failed.
        {
            AfxMessageBox(_T("Error creating Dialog"));
        }    

        m_pDialog->ShowWindow(SW_SHOW);
    }
    else
    {
        // cannot check if visible at the beginning of method because
        // pointer could be NULL and will throw an exception

        if(m_pDialog->IsWindowVisible())
        {
            return;
        }

        m_pDialog->DestroyWindow();
        m_pDialog = NULL;
        MenuItemMethod();
    }
}

I just ended up checking if the modeless dialog is visible after clicking on the form's menu item again. If it is visible, don't do anything. If not, destroy the existing non-visible dialog, set the pointer to NULL, and recursively call the method again. Since the pointer is now NULL, it should recreate the dialog normally and then return to normal operation.

0
votes

you have to delete the memory in PostNcDestroy like this

void CChildDialog ::PostNcDestroy() 
{   
    CDialog::PostNcDestroy();
    GetParent()->PostMessage(WM_WIN_CLOSED,0,0);
    delete this;
}

and send a user defined message to the parent window that your window is closed. In the parent window add a message handler for WM_WIN_CLOSED like

LRESULT CMainDialog::OnMyMethod(WPARAM wParam, LPARAM lParam)
{
    m_pDialog = NULL; 
    return 0;
}