首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

MFC 無模式對話框建立與生命週期管理

     編輯:關於C語言

無模式 vs 有模式:核心差異

特性有模式對話框無模式對話框
建立函式DoModal()Create() + ShowWindow()
程式流程阻塞呼叫執行緒,直到關閉立即返回,主視窗繼續運作
物件壽命通常用區域變量(棧上)需動態配置(堆上),手動銷毀
父視窗關係自動停用父視窗父視窗保持啟用,可並行操作

標準建立流程

void CYourView::OnOpenModelessDlg()
{
    // 1. 堆上建立對話框物件
    CTestDlg* pDlg = new CTestDlg;

    // 2. 從資源範本建立視窗(尚未顯示)
    if (!pDlg->Create(IDD_TEST_DLG, this))  // this 為父視窗指標
    {
        delete pDlg;  // 建立失敗需手動釋放
        return;
    }

    // 3. 顯示視窗
    pDlg->ShowWindow(SW_SHOW);

    // 4. 【關鍵】不要在此處 delete pDlg!
    //    物件由對話框內部自行銷毀(見下文)
}

生命週期管理:誰負責刪除?

無模式對話框物件必須在視窗銷毀時自我刪除,兩種主流模式:

模式一:覆寫 OnOK/OnCancel + WM_DESTROY 自刪

// CTestDlg.h
class CTestDlg : public CDialog
{
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnOK();
    afx_msg void OnCancel();
    afx_msg void OnDestroy();
};

// CTestDlg.cpp
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
    ON_WM_DESTROY()
END_MESSAGE_MAP()

void CTestDlg::OnOK()
{
    CDialog::OnOK();      // 執行預設行為(UpdateData 等)
    DestroyWindow();      // 強制銷毀視窗,觸發 WM_DESTROY
}

void CTestDlg::OnCancel()
{
    CDialog::OnCancel();
    DestroyWindow();
}

void CTestDlg::OnDestroy()
{
    CDialog::OnDestroy();
    delete this;          // 【核心】自我刪除 C++ 物件
}

模式二:父視窗持有指標,關閉時統一清理

// 父視窗類別(如 CMainFrame 或 CView)
class CParentWnd : public CFrameWnd
{
    CTestDlg* m_pModelessDlg;  // 成員指標
    // ...
};

void CParentWnd::OnOpenDlg()
{
    if (m_pModelessDlg == nullptr)
    {
        m_pModelessDlg = new CTestDlg;
        m_pModelessDlg->Create(IDD_TEST_DLG, this);
        m_pModelessDlg->ShowWindow(SW_SHOW);
    }
    else
    {
        m_pModelessDlg->SetForegroundWindow();  // 已存在則帶到前景
    }
}

// 父視窗關閉時清理
void CParentWnd::OnClose()
{
    if (m_pModelessDlg)
    {
        m_pModelessDlg->DestroyWindow();  // 觸發對話框 OnDestroy
        m_pModelessDlg = nullptr;         // 指標置空,防止野指標
    }
    CFrameWnd::OnClose();
}

// 對話框 OnDestroy 僅置空父視窗指標,不自刪
void CTestDlg::OnDestroy()
{
    CDialog::OnDestroy();
    // 通知父視窗(可用 PostMessage 自訂訊息)
    GetParent()->PostMessage(WM_USER_DLG_CLOSED, 0, (LPARAM)this);
    // 不要 delete this!
}

關鍵 API 對照表

函式用途備註
Create(lpTemplateName, pParentWnd)建立視窗(不顯示)資源 ID 或範本指標
ShowWindow(nCmdShow)顯示/隱藏視窗SW_SHOW/SW_HIDE
DestroyWindow()銷毀視窗,發送 WM_DESTROY非同步,立即返回
IsWindow(m_hWnd)檢查視窗是否存在避免對已銷毀物件操作
SetForegroundWindow()帶到前景並啟用適用單一實例模式

常見坑與最佳實務

  • 棧上建立無模式對話框:函式返回時物件被銷毀,視窗句柄失效 → 崩潰。必須用 new 動態配置。
  • 父視窗指標傳 NULL:對話框會游離於工作列,無法隨主視窗最小化/還原。請傳 this 或 AfxGetMainWnd()。
  • 重複開啟同一對話框:使用成員指標檢查 if (m_pDlg == nullptr) 或 IsWindow(m_pDlg->m_hWnd)。
  • 跨執行緒操作 UI:無模式對話框屬於建立它的執行緒,其他執行緒需用 PostMessage 通信。
  • 資源洩漏:若對話框內有 new 出的子物件,需在 OnDestroy 或解構子釋放。

進階:無模式對話框作為子視窗嵌入

資源編輯器中設定對話框屬性:

  • Style:Child
  • Border:None
  • Visible:Unchecked(由程式控制顯示)
// 父視窗 OnCreate 中嵌入
int CParentView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1) return -1;

    m_pChildDlg = new CChildDlg;
    m_pChildDlg->Create(IDD_CHILD_DLG, this);
    m_pChildDlg->SetWindowPos(nullptr, 0, 0, 400, 300, SWP_NOZORDER | SWP_SHOWWINDOW);
    return 0;
}

// 父視窗大小改變時調整子對話框
void CParentView::OnSize(UINT nType, int cx, int cy)
{
    CView::OnSize(nType, cx, cy);
    if (m_pChildDlg && ::IsWindow(m_pChildDlg->m_hWnd))
        m_pChildDlg->MoveWindow(0, 0, cx, cy);
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved