MFC 主程序显示 模态对话框
两种创建模式完整可运行代码
前置约定
- 子对话框类:
CChildDlg(Child 无边框样式,用于嵌入 Static 容器) - 容器控件 ID:
IDC_CONTAINER(非默认 IDC_STATIC) - 父窗口类:
CMainDlg
模式 1:栈对象成员变量(推荐,常驻嵌入)
1. 父窗口头文件 CMainDlg.h
cpp
运行
#include "ChildDlg.h" class CMainDlg : public CDialogEx { // ... private: // 栈对象,全局生命周期,无需手动释放 CChildDlg m_childDlg; protected: virtual BOOL OnInitDialog(); afx_msg void OnSize(UINT nType, int cx, int cy); DECLARE_MESSAGE_MAP() };2. 父窗口 OnInitDialog 创建嵌入窗口
cpp
运行
BOOL CMainDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 获取容器控件坐标 CWnd* pContainer = GetDlgItem(IDC_CONTAINER); CRect rcContainer; pContainer->GetWindowRect(&rcContainer); ScreenToClient(&rcContainer); // 创建子对话框(仅执行一次) m_childDlg.Create(IDD_CHILD_DLG, this); // 绑定容器作为父窗口,真正嵌入 m_childDlg.SetParent(pContainer); // 填满容器区域 m_childDlg.MoveWindow(0, 0, rcContainer.Width(), rcContainer.Height()); m_childDlg.ShowWindow(SW_SHOW); return TRUE; }3. 窗口缩放自适应 OnSize
cpp
运行
void CMainDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); // 判断子窗口是否创建成功 if (!m_childDlg.GetSafeHwnd()) return; CWnd* pContainer = GetDlgItem(IDC_CONTAINER); CRect rcClient; pContainer->GetClientRect(&rcClient); m_childDlg.MoveWindow(&rcClient); }4. 手动隐藏 / 销毁(可选)
cpp
运行
// 仅隐藏,保留句柄,下次直接Show m_childDlg.ShowWindow(SW_HIDE); m_childDlg.ShowWindow(SW_SHOW); // 彻底销毁(一般不需要,析构自动释放) m_childDlg.DestroyWindow();优点
栈对象随父窗口自动析构,无内存泄漏;代码极简,适合常驻内嵌界面。
模式 2:堆指针(动态创建 / 销毁,按需切换界面)
1. 父窗口头文件 CMainDlg.h
cpp
运行
#include "ChildDlg.h" class CMainDlg : public CDialogEx { // ... private: // 堆指针,手动管理生命周期 CChildDlg* m_pChildDlg = nullptr; protected: virtual BOOL OnInitDialog(); afx_msg void OnBtnCreateChild(); afx_msg void OnBtnDestroyChild(); virtual ~CMainDlg(); // 析构兜底释放 DECLARE_MESSAGE_MAP() };2. 动态创建子窗口(按钮触发)
cpp
运行
void CMainDlg::OnBtnCreateChild() { // 防止重复创建 if (m_pChildDlg != nullptr && m_pChildDlg->GetSafeHwnd()) return; CWnd* pContainer = GetDlgItem(IDC_CONTAINER); CRect rcContainer; pContainer->GetWindowRect(&rcContainer); ScreenToClient(&rcContainer); // new 堆分配 m_pChildDlg = new CChildDlg(this); m_pChildDlg->Create(IDD_CHILD_DLG, this); m_pChildDlg->SetParent(pContainer); m_pChildDlg->MoveWindow(0, 0, rcContainer.Width(), rcContainer.Height()); m_pChildDlg->ShowWindow(SW_SHOW); }3. 手动销毁子窗口(按钮触发)
cpp
运行
void CMainDlg::OnBtnDestroyChild() { if (m_pChildDlg == nullptr) return; // 销毁窗口 + 释放堆内存 m_pChildDlg->DestroyWindow(); delete m_pChildDlg; m_pChildDlg = nullptr; }4. 父窗口析构函数(兜底防内存泄漏)
cpp
运行
CMainDlg::~CMainDlg() { // 窗口关闭时强制释放 if (m_pChildDlg != nullptr) { m_pChildDlg->DestroyWindow(); delete m_pChildDlg; m_pChildDlg = nullptr; } }5. 自适应缩放 OnSize(同栈对象逻辑)
cpp
运行
void CMainDlg::OnSize(UINT nType, int cx, int cy) { CDialogEx::OnSize(nType, cx, cy); if (m_pChildDlg == nullptr || !m_pChildDlg->GetSafeHwnd()) return; CWnd* pContainer = GetDlgItem(IDC_CONTAINER); CRect rcClient; pContainer->GetClientRect(&rcClient); m_pChildDlg->MoveWindow(&rcClient); }适用场景
需要频繁切换、打开 / 关闭内嵌子界面;多个不同子对话框动态切换容器。
两种模式核心区别总结
表格
| 模式 | 内存管理 | 创建时机 | 销毁方式 | 适用场景 |
|---|---|---|---|---|
| 栈成员变量 | 自动释放,无泄漏 | 父窗口初始化一次性创建 | 父窗口销毁自动释放 | 长期固定内嵌界面 |
| 堆指针 new | 必须手动 delete,否则泄漏 | 按钮 / 事件按需创建 | 手动 DestroyWindow+delete | 动态切换、临时内嵌界面 |
关键注意事项
- 子对话框资源必须设置:
Style=Child、Border=None,否则无法嵌入; - 容器控件 ID 不能是默认
IDC_STATIC,否则无法获取句柄; - 模态对话框
DoModal()不能嵌入控件,仅支持Create()非模态子窗口。
