0
votes

I've created an MFC Application, with a Base Dialog (derived from CDialog class), a Setting Dialog (derived from CBaseDlg and an App Dialog (also derived from CBaseDlg). Then I created a class called CScrMng (aka Screen Manager) that hold the ShowDialog() function (to Create and Show these dialogs).

The basic idea is ScrMng will manage all of my Modeless Dialogs, and anytime I want to open a dialog, I just need to CScrMng::ShowDialog() in the BaseDlg.cpp, and the dialog will display.

This approach has caused resources to leak here and there. I've done a bit of research about overriding the PostNcDestroy(), but I don't have a clear idea of where to call it.

What function should I use to properly close these modeless dialogs?

I want to open the Setting Dialog from Base Dialog, then when I click on the Cancel button, it should return me to the Base Dialog screen so that I can open another Dialog.

Right now I'm using EndDialog(). I know it's wrong, but calling DestroyWindow() will immediately exit the program, which is not what I want.

Source code

MFCApplication.cpp


#include...

BEGIN_MESSAGE_MAP(CMFCApp, CWinApp)
    ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()

CMFCApp::CMFCApp()
{
     // support Restart Manager
     m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
}

CMFCApp theApp;

BOOL CMFCApp::InitInstance()
{
    ...
    CWinApp::InitInstance();

    CShellManager *pShellManager = new CShellManager;

    CScrMng::GetInstance()->ShowDialog(IDD_MAINDLG);

    SetRegistryKey(_T("Local Applications"));

    if (pShellManager != NULL)
    {
        delete pShellManager;
    }
    return TRUE;
}

CScrMng.cpp

#include...

CScrMng* CScrMng::m_pInstance = NULL;
CScrMng* CScrMng::GetInstance(){
     if (m_pInstance == NULL)
    m_pInstance = new CScrMng();
    return m_pInstance;
}

CScrMng::CScrMng(){}

void CScrMng::ShowDialog(int ID)
{
    CMainDlg* m_pDlg = NULL;
    switch (ID)
    {
    case IDD_MAINDLG:
        m_pDlg = new CMainDlg();
        theApp.m_pMainWnd = m_pDlg;
        m_pDlg->Create(IDD_MAINDLG);
        m_pDlg->ShowWindow(SW_SHOW);
        m_pDlg->UpdateWindow();
        break;

    case ...
        break;

    case IDD_SETTINGDLG:
        m_pDlg = new CSettingDlg();
        m_pDlg->Create(ID,NULL);
        m_pDlg->ShowWindow(SW_SHOW);
        m_pDlg->UpdateWindow();
        break;
    }

CMainDlg.cpp

#include...

CMainDlg::CMainDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CMainDlg::IDD, pParent) {m_hIcon = AfxGetApp()-> LoadIcon(IDR_MAINFRAME);}

void CMainDlg::DoDataExchange(CDataExchange* pDX) {...}

void CMainDlg::PostNcDestroy() //Added these
{
    CDialog::PostNcDestroy();
    delete this;
}

BEGIN_MESSAGE_MAP(CMainDlg, CDialog)
...
END_MESSAGE_MAP()

BOOL CMainDlg::OnInitDialog() {
    CDialog::OnInitDialog();
    SetIcon(m_hIcon, FALSE);        
    return TRUE; 
}

void CMainDlg::OnPaint() {...}

void CMainDlg::OnBnClickedOpenAppdlg()
{
    CScrMng::GetInstance()->ShowDialog(IDD_APPDLG);
}

void CMainDlg::OnBnClickedOpenSettingdlg()
{
    CScrMng::GetInstance()->ShowDialog(IDD_SETTINGDLG);
}

void CMainDlg::OnBnClickedExit() 
{
     DestroyWindow(); //replaced CDialog::OnCancel() with this.
}

Update: After changing the code in the SettingDlg.cpp, i encountered a Debug Assertion Failed! problem :

void CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint)
{
    ASSERT(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL)); //Breakpoint triggered

    if (m_pCtrlSite == NULL)
        ::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint);
    else
        m_pCtrlSite->MoveWindow(x, y, nWidth, nHeight);
}

Here are what i changed in the .cpp file: SettingDlg.cpp

void CSettingDlg::PostNcDestroy()
{
    CMainDlg::PostNcDestroy();
}

void CSettingDlg::OnBnClickedSettingcancel()
{
    DestroyWindow(); //Using destroyWindow rather than EndDialog();
}
1
You don't call PostNCDestroy, the framework calls it. Read this and thisJabberwocky
@Jabberwocky can you give me some advices on how should i close the modeless dialog in my application ?Turtle Lover
An approach often used with a modeless dialog box is to create it and then either show or hide it rather than create and delete it. So the modeless dialog box is created once and then what the user sees is the modeless dialog box is either displayed or not. On the other hand this may just be an exercise and investigation into modeless dialog boxes so feel free to ignore this comment.Richard Chambers
This line of source theApp.m_pMainWnd = m_pBaseDlg; in the function CScrMng::ShowDialog() should not be there. The theApp.m_pMainWnd should be auto populated by the framework and you should not change it since that is the main window of the application.Richard Chambers
@RichardChambers these lines of codes isn't mine. it's from my MFC instructor at my company (i'm an intern). he's code this for me. So, in anychance that you can teach me how to fix tthe code above without changing it too much ?Turtle Lover

1 Answers

3
votes

Close & Delete a modeless Dialogs.
A proper Way is: Override PostNcDestroy, OnOk() and OnCancel() for the Modeless Dialogs

void CBaseDlg::PostNcDestroy()
{
    CDialog::PostNcDestroy();
    delete this;
}

.

void CBaseDlg::OnOk()
{
   if(!UpdateData(TRUE))
       return;
    DestroyWindow();
}

.

void CBaseDlg::OnCancel()
{
    DestroyWindow();
}