对话框通信






2.77/5 (59投票s)
关于数据交换的艺术



引言
我们经常需要从一个对话框与另一个对话框进行通信。我将以此为中心,使用两个经典的 MFC CDialog
继承类来演示,每个类都是模态的。
并且,由于我更倾向于先编写可运行的代码,然后再美化它,我将提供编写良好的代码,使读者更容易理解。请注意,我并不自诩编码完美,也没有遵循 THE 方法论。我只想给我的读者(主要是初学者)一个坚实的基础,让他们自己理解并编写代码。
不再多言,现在让我们进入代码。
起点:我感到孤独,我需要一个孩子
好的,这是一个相当遥远的需求起源。无论如何,我得到了这个机会来向您介绍这些角色。
CParentDlg
:父对话框
CChildDlg
:子对话框
class CParentDlg : public CDialog
{
CButton m_btnOpenChild;
//The button that will open the child dialog
CEdit m_edtCommentText;
//Used to transmit a comment to the child
CButton m_chkCaptainBlind;
//Same with a boolean to indicate if the captain is blind
public:
CParentDlg();
protected:
afx_msg void OnOpenChild();
//Even Handler of the m_btnOpenChild button click
};
class CChildDlg : public CDialog
{
CEdit m_edtCommentText; //The comment received
CButton m_chkCaptainBlind; //And the boolean received
CString m_strComment;
bool m_bIsCaptainBlind;
public:
CChildDlg(const CString& strComment, bool bIsCaptainBlind);
const CString& GetComment() const;
bool IsCaptainBlind() const;
protected:
virtual BOOL OnInitDialog();
//Used to initialize the controls before the dialog
// is displayed at its first time afx_msg void OnOK();
afx_msg void OnOK();
//Even Handler of the OK button click
};
请注意,类声明已被简化,仅显示重要的点。因此,IDD
枚举器、DoDataExchange()
、OnQueryDragIcon()
、OnSysCommand()
、OnPaint()
和 DECLARE_MESSAGE_MAP()
已实现但在此处隐藏。但是,我保留了 OnInitDialog()
,因为它很快将扮演主要角色; 就像类构造函数一样。
现在,当按下父对话框上的按钮时,我们可以打开子对话框。
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());
}
}
简而言之,该函数首先获取 EditBox 的内容和 CheckBox 的状态。一旦收集了这些信息,我们就将它们传递给子对话框并显示它。现在,子对话框负责数据的完整性。在我们的例子中,父对话框将获得这些值,因为子对话框选择了“确定”,否则,什么都不做。
哦,亲爱的宝贝,现在我们可以说话了
请注意上面 OnOpenChild()
函数中的两个重要行:调用子对话框的构造函数,以及使用 DoModal()
开始实际显示。
让我们详细说明这里发生了什么。
构造:提供信息
您肯定已经注意到了构造函数调用中的参数。以下是我们可以定义子对话框的方式
CChildDlg::CChildDlg(const CString& strComment, bool bIsCaptainBlind)
{
this->m_strComment = strComment;
this->m_bIsCaptainBlind = bIsCaptainBlind;
}
现在,我们将数据存储在子对话框类中。但我们仍然需要正确显示它们。
显示:显示信息
我们不直接编写 DoModal()
函数。这个函数是从 CDialog
类继承而来的,我们不需要知道它做了什么,除了它构造一个模态对话框,在此过程中执行我们的 OnInitDialog()
BOOL CChildDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_edtCommentText.SetWindowText(m_strComment);
m_chkCaptainBlind.SetCheck(m_bIsCaptainBlind);
return TRUE;
}
让用户玩,并在他离开时给我反馈
对我们(作为开发人员)来说,除了等待之外,没有什么可做的。
子对话框已被构造、初始化然后显示。用户现在正在使用 UI,修改我们的初始注释并选中/取消选中我们的 CheckBox。
可以添加一些事件处理程序,但这不是重点。我们只是涵盖父对话框与其子对话框之间的通信。
然后我们等待直到子对话框关闭。在这里,有两种可能性:通过“确定”验证更改,或取消(使用同名的按钮)。
在这里,我选择让用户更改他想要的内容,并且只有在按下“确定”后,这两个数据成员才会更新。为此,我在“确定”按钮上添加了一个事件处理程序。
void CChildDlg::OnOK()
{
m_edtCommentText.GetWindowText(m_strComment);
m_bIsCaptainBlind = m_chkCaptainBlind.GetCheck();
EndDialog(IDOK);
}
为什么我们需要这个?因为父对话框将使用 GetComment()
和 IsCaptainBlind()
访问器来恢复这些成员的内容。
嘿,爸爸,这是你的数据
让我们跳回 OnOpenChild()
父处理程序的结尾
if (nResponse == IDOK)
{
m_edtCommentText.SetWindowText(dlg.GetComment());
m_chkCaptainBlind.SetCheck(dlg.IsCaptainBlind());
}
当按下“确定”按钮时,我们现在进入 if 语句,并最终更新父 UI。
摘要
这是一个相当简单的例子。在现实世界中,您通常需要根据情况交换大量数据。
通常的做法是将所有需要交换的数据封装在一个为此定义的类中,并传递该类的单个实例。