I am creating a modeless dialog box. The dialog box is called from the menu item of main frame window.
MainFrm.h
CModeless* modeless;
bool modelessDlgOpen;
MainFrm.cpp
void CMainFrame::OnDatabaseMLdlg()
{
// TODO: Add your command handler code here
if (modelessDlgOpen == TRUE)
return;
modelessDlgOpen = TRUE;
modeless = new CModeless(this);
//modeless->Create(IDD_MLDLG, GetDesktopWindow());
modeless->Create(IDD_MLDLG, this);
mbPoll->ShowWindow(SW_SHOW);
}
When menu item is clicked, OnDatabaseMLdlg() function is called and a modeless dialog box with resource ID IDD_MLDLG appears.
The issue is while closing the modeless dialog box.
I am not able to find out the correct method to have a clean closure / destroy of this modeless dialog box. Upon clicking the cross button in right-top corner, which message gets
generated?
My current code which I have tried is as follows. (producing code related only to the closure of the dialog box)
MLDLG.h
#pragma once
#define WM_MLDLG_CLOSED (WM_USER + 555)
// CModeless dialog
class CModeless : public CDialog
{
DECLARE_DYNAMIC(CModeless)
public:
CModeless(CWnd* pParent = NULL); // standard constructor
virtual ~CModeless();
// Dialog Data
enum { IDD = IDD_MLDLG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
public:
virtual BOOL Create(UINT nIDTemplate, CWnd* pParentWnd = NULL);
afx_msg void OnNcDestroy();
virtual void PostNcDestroy();
CWnd* mParent;
afx_msg void OnBnClickedCancel();
};
MLDLG.cpp
void CModeless::OnNcDestroy()
{
CDialog::OnNcDestroy();
// TODO: Add your message handler code here
}
void CModeless::PostNcDestroy()
{
CDialog::PostNcDestroy();
GetParent()->PostMessage(WM_MLDLG_CLOSED,0,0); // **CRASHES HERE**
delete this;
}
void CModeless::OnBnClickedCancel()
{
// TODO: Add your control notification handler code here
//CDialog::OnCancel();
DestroyWindow();
}
Not able to understand what am I doing wrong or what am I missing?
I can provide additional details in case required.
Thanks in advance.
EDIT-20130612: Additional information:
My constructor is as follows:
CModeless::CModeless(CWnd* pParent /*=NULL*/)
: CDialog(CModeless::IDD, pParent)
{
mParent = pParent;
if (mParent == NULL)
{
MessageBox(L"mParent is NULL");
}
else
{
MessageBox(L"mParent is not NULL");
}
}
Here, I have verified that mParent is not NULL.
PostNCDestroy() is called VERY late and most of the useful state of the MFC window is not valid at that point. GetParent() is probably returning NULL, which will cause a crash the way you are using it.
Try moving the PostMessage call to OnDestroy() before calling the base class implementation there.
Another option is to cache the parent's hWnd and call ::PostMessage() on that hWnd;
Related
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();
}
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();
}
I have simple FMC dialog form. Can't find how to manage event that should be called on form create moment. Something like onFormCreate like it is in VB or Delphi.
How to create such functionality?
My simple form header:
#pragma once
// CMFCApplicationUPTDlg dialog
class CMFCApplicationUPTDlg : public CDialog
{
// Construction
public:
CMFCApplicationUPTDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
enum { IDD = IDD_MFCAPPLICATIONUPT_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedOk();
afx_msg void OnBnClickedButton1();
};
You want to handle it in the OnInitDialog() member function. At the point that OnInitDialog() is called, the dialog and all of its child windows will have been created. However, the dialog will still be invisible to the user. After exiting OnInitDialog(), the dialog will be shown to the user.
So, it is the place where you would want to do initialization of your dialog.
You need to implement OnInitDialog in your own derived class.
BOOL CMFCApplicationUPTDlg::OnInitDialog()
{
// this will create the controls you defined in the resources.
BOOL retValue = __super::OnInitDialog();
// initialize (or add new controls to ) your dialog content here.
// ...
return retValue;
}
I have a mainWindow class that calls a function mainWIndow::ShowDialogBox() when double clicked on the QTabBar. The dialog box shows up, but it isn't connecting the buttons. I have the connect calls in ShowDialogBox. It gives me a red underline on connect saying
no instance of overloaded function "MainWindow::connect" matches the argument list"
This is my code
bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
if (object == mTabWidget->getTabBar() && event->type() == QEvent::MouseButtonDblClick)
{
qDebug()<<"dblclk";
ShowDialogBox();
}
return QObject::eventFilter(object, event);
}
//Show dialog box when double clicked on QTabBar
void MainWindow::ShowDialogBox(){
QDialog dialog;
QVBoxLayout layout(&dialog);
QLineEdit editLine;
layout.addWidget(&editLine);
QDialogButtonBox *dialogButton = new QDialogButtonBox(QDialogButtonBox::Ok );
connect(dialogButton, SIGNAL(accepted()), dialog, SLOT(accept())); //this 'connect' is underlined
layout.addWidget(dialogButton);
dialog.setLayout(&layout);
if(dialog.exec() == QDialog::Accepted)
{
mTabWidget->setTabText(0, editLine.text());
}
}
I have added the signals and slot in mainWindow.h as
private slots:
void accept();
signals:
void accepted();
I have spend hours on this but no luck. I am new to Qt.
Line:
connect(dialogButton, SIGNAL(accepted()), dialog, SLOT(accept()));
should be:
connect(dialogButton, SIGNAL(accepted()), &dialog, SLOT(accept()));
Because the third parameter has to be a memory address(pointer).
I need to set dialog form caption. I was trying to create CString variable that might be binded to caption with Class Wizard. But there is no main form control in selection menu. What is the way of doing that?
This is my dialog form:
#include "stdafx.h"
#include "MyDlg.h"
#include "afxdialogex.h"
// MyDlg dialog
IMPLEMENT_DYNAMIC(MyDlg, CDialog)
MyDlg::MyDlg(CWnd* pParent /*=NULL*/)
: CDialog(MyDlg::IDD, pParent)
, m_edit(_T(""))
{
}
MyDlg::~MyDlg()
{
}
void MyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, m_edit);
}
BEGIN_MESSAGE_MAP(MyDlg, CDialog)
ON_BN_CLICKED(IDOK, &MyDlg::OnBnClickedOk)
END_MESSAGE_MAP()
// MyDlg message handlers
void MyDlg::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
CDialog::OnOK();
txt=m_edit;
}
This is code that creates dialog:
BOOL CPreparationApp::InitInstance()
{
MyDlg Dlg;
//how to tell Dlg to have form caption "BLABLABLA"?
Dlg.DoModal();
return TRUE;
}
Hoping I've understood your question in the right way:
// MyDlg.h
class MyDlg
{
public: // private is fine too if you're OOP nazi but you have to provide a SetDlgCaption method then.
CString m_strDlgCaption;
};
// MyDlg.cpp
BOOL MyDlg::OnInitDialog( )
{
SetWindowText( m_strDlgCaption );
}
BOOL CPreparationApp::InitInstance()
{
MyDlg Dlg;
Dlg.m_strDlgCaption = _T("A fancy caption for your dialog");
Dlg.DoModal();
}
If you have not already done it you first need to add an override in the dialog class for OnInitDialog. This is the first place you can execute code after the dialog and its control windows exist. You can put SetWindowText(_T("BLABLABLA")) in OnInitDialog to set the dialog caption.
I have the following code in trying to set the text in a Cedit text box:
class CMetaDlg : public CDialogEx
{
public:
CMetaDlg();
// Dialog Data
enum { IDD = IDD_META };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
public:
CEdit m_author;
CEdit m_sources;
afx_msg void OnBnClickedOk();
};
CMetaDlg::CMetaDlg() : CDialogEx(CMetaDlg::IDD)
{
}
void CMetaDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_AUTHOR, m_author);
DDX_Control(pDX, IDC_SOURCES, m_sources);
}
BEGIN_MESSAGE_MAP(CMetaDlg, CDialogEx)
ON_BN_CLICKED(IDOK, &CMetaDlg::OnBnClickedOk)
END_MESSAGE_MAP()
void CEmergenceApp::OnFileMeta()
{
CMetaDlg md;
md.DoModal();
md.m_author.SetWindowTextW(CEmergenceView::GetDoc()->author);
md.m_sources.SetWindowTextW(CEmergenceView::GetDoc()->sources);
}
This gives me a debug assertion error. I am assuming the problem lies in the lines:
md.m_author.SetWindowTextW(CEmergenceView::GetDoc()->author);
md.m_sources.SetWindowTextW(CEmergenceView::GetDoc()->sources);
As commenting them out, everything works fine.
When you call DoModal the dialog is created, the edit controls are created and then when the user clicks OK or Cancel the edit controls and dialog window are destroyed. Then DoModal returns. I do not understand what you are trying to do by trying to set the edit control's text after the dialog box closes.