Introduction
We often need to communicate from one dialog to another. I will center this demonstration using two classic MFC CDialog
inherited classes, each of which is modal.
And as I'm far from being partisan to writing working code first, then beautifying it, I will provide well written code to make this more accessible to readers. Please notice that I don't pretend to code perfectly, nor having THE methodology to follow. I only want to give to my readers (mostly beginners) a strong foot to understand and write code by themselves.
Without further argumentation, let's jump now into the code.
Starting point: I feel lonely, I need a child
Ok, That's a pretty far origin of the need. Anyway, the occasion is given to me then to introduce you to the characters.
CParentDlg
: The parent dialog
CChildDlg
: The child dialog
class CParentDlg : public CDialog
{
CButton m_btnOpenChild;
CEdit m_edtCommentText;
CButton m_chkCaptainBlind;
public:
CParentDlg();
protected:
afx_msg void OnOpenChild();
};
class CChildDlg : public CDialog
{
CEdit m_edtCommentText; CButton m_chkCaptainBlind; CString m_strComment;
bool m_bIsCaptainBlind;
public:
CChildDlg(const CString& strComment, bool bIsCaptainBlind);
const CString& GetComment() const;
bool IsCaptainBlind() const;
protected:
virtual BOOL OnInitDialog();
afx_msg void OnOK();
};
Note that the class declarations are lightened to show only the important points. Due to this, the IDD
enumerator, DoDataExchange()
, OnQueryDragIcon()
, OnSysCommand()
, OnPaint()
and DECLARE_MESSAGE_MAP()
are implemented but hidden here. However, I keep the OnInitDialog()
present because it will soon play a major role; as much as the class constructors.
Now we can open the child dialog when the button is pressed on the parent.
void CParentDlg::OnOpenChild()
{
CString strComment;
m_edtCommentText.GetWindowText(strComment);
bool bIsCaptainBlind = m_chkCaptainBlind.GetCheck();
CChildDlg dlg(strComment, bIsCaptainBlind);
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
m_edtCommentText.SetWindowText(dlg.GetComment());
m_chkCaptainBlind.SetCheck(dlg.IsCaptainBlind());
}
}
In brief, the function firstly gets the content of the EditBox and the state of the CheckBox. Once these pieces of information are collected, we pass them to the child dialog and display it. The child is now responsible for the integrity of the data. In our example, the Parent will get the values since the child is left with OK, otherwise, nothing more is done.
Oh sweet baby, now we can talk
Notice the two important lines in the OnOpenChild()
function above: the calling of the constructor of the child, and the start of the actual display with DoModal()
.
Let's detail what's happening here.
Construction: giving the information
You have certainly noticed the parameters in the contructor call. Here is how we could have defined the child dialog:
CChildDlg::CChildDlg(const CString& strComment, bool bIsCaptainBlind)
{
this->m_strComment = strComment;
this->m_bIsCaptainBlind = bIsCaptainBlind;
}
Now we have the data stored in the child dialog class. But we still have to display them properly.
Display: showing the information
We don't directly code the DoModal()
function. This one is inherited from the CDialog
class and we don't need to know what it does except the fact that it constructs a modal dialog box, executing in this process our OnInitDialog()
:
BOOL CChildDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_edtCommentText.SetWindowText(m_strComment);
m_chkCaptainBlind.SetCheck(m_bIsCaptainBlind);
return TRUE;
}
Let the user play, and give me feedback when he's gone
For us (as developers), there is nothing much to do other than to wait.
The child dialog has been constructed, initialized, then displayed. The user is now playing with the UI, modifying our initial comment and checking/unchecking our CheckBox.
One could have added some event handlers, but that's not the point here. We're only covering the communication between the parent and its child.
We then wait until the child is closed. Here, there are two possibilities of doing so: Validating the changes made by OK, or cancelling (with the button of the same name).
Here, I choose to let the user change what he wants, and only if OK is pressed, the two data members are updated. For this, I added an event handler on the OK button.
void CChildDlg::OnOK()
{
m_edtCommentText.GetWindowText(m_strComment);
m_bIsCaptainBlind = m_chkCaptainBlind.GetCheck();
EndDialog(IDOK);
}
Why do we need this? Because it is the content of those members that the parent will recover using GetComment()
and IsCaptainBlind()
accessors.
Here dad, your data
Let's jump back to the end of OnOpenChild()
parent handler:
if (nResponse == IDOK)
{
m_edtCommentText.SetWindowText(dlg.GetComment());
m_chkCaptainBlind.SetCheck(dlg.IsCaptainBlind());
}
When the button OK is pressed, we now get into the if statement, and finally update the parent UI.
Summary
That was a pretty light example. In the real world, you will often need to exchange a lot of data depending on the case.
What is usually done is encapsulating all the data that needs to be exchanged into a class defined for it, and transmiting a single instance of that class.