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

MFC 對話框控制項屬性修改時機解析:建構函數 vs OnInitDialog vs DoModal/Create

     編輯:VC 教學

常見錯誤現場


// 錯誤範例:在建構對話框物件後、DoModal 前直接設定控制項
void CMyView::OnShowDialog()
{
    CMyDialog dlg;           // 1. 建構對話框物件
    dlg.m_btnOK.SetWindowText(_T("確定送出")); // 2. 嘗試修改按鈕文字 → ASSERT 失敗!m_hWnd 為 NULL
    dlg.DoModal();           // 3. 顯示模態對話框
}

除錯斷點停在 ASSERT(::IsWindow(m_hWnd)),m_hWnd == 0x00000000。原因:**控制項視窗尚未建立**。

根本原因:對話框物件 ≠ 對話框視窗

三種正確修改控制項屬性的時機

時機 1:在 OnInitDialog 中修改 (最推薦,模態/非模態通用)


// MyDialog.cpp
BOOL CMyDialog::OnInitDialog()
{
    CDialog::OnInitDialog(); // 必須先呼叫基類

    // 此時所有控制項視窗已建立,可直接操作
    m_btnOK.SetWindowText(_T("確定送出"));
    m_editName.SetLimitText(20);
    m_comboType.AddString(_T("類型 A"));
    m_comboType.AddString(_T("類型 B"));
    m_comboType.SetCurSel(0);

    return TRUE;
}

時機 2:非模態對話框 Create() 之後修改


// 非模態對話框建立流程
void CMyView::OnShowModelessDialog()
{
    // 1. 建立物件 (堆疊或堆積)
    CMyDialog* pDlg = new CMyDialog(this);

    // 2. 建立視窗 (關鍵步驟!)
    if (!pDlg->Create(IDD_MYDIALOG, this))
    {
        delete pDlg;
        return;
    }

    // 3. 視窗建立完成,控制項可用
    pDlg->m_btnOK.SetWindowText(_T("關閉視窗"));
    pDlg->m_editName.SetWindowText(_T("預設值"));

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

時機 3:透過資料成員 + DDX/DDV 間接修改 (解耦合、支援序列化)


// MyDialog.h
class CMyDialog : public CDialog
{
    // ...
protected:
    CString m_strBtnText; // 對應按鈕文字
    CString m_strEditVal; // 對應編輯框內容
    // DDX 映射
    //{{AFX_DATA(CMyDialog)
    //    DDX_Text(pDX, IDC_EDIT_NAME, m_strEditVal);
    //}}AFX_DATA
public:
    void SetButtonText(LPCTSTR lpszText) { m_strBtnText = lpszText; }
    void SetEditValue(LPCTSTR lpszVal)   { m_strEditVal = lpszVal; }
};

// MyDialog.cpp
BOOL CMyDialog::OnInitDialog()
{
    CDialog::OnInitDialog();
    // DDX 自動將資料成員套用至控制項
    UpdateData(FALSE); // FALSE: 成員變數 → 控制項
    return TRUE;
}

// 外部呼叫
void CMyView::OnShowDialog()
{
    CMyDialog dlg;
    dlg.SetButtonText(_T("自訂按鈕"));
    dlg.SetEditValue(_T("初始內容"));
    dlg.DoModal(); // OnInitDialog 會自動套用
}

進階模式:建構函數參數化初始化


// MyDialog.h
class CMyDialog : public CDialog
{
public:
    CMyDialog(LPCTSTR lpszBtnText, LPCTSTR lpszEditVal, CWnd* pParent = NULL);
    // ...
private:
    CString m_strBtnText;
    CString m_strEditVal;
};

// MyDialog.cpp
CMyDialog::CMyDialog(LPCTSTR lpszBtnText, LPCTSTR lpszEditVal, CWnd* pParent)
    : CDialog(IDD_MYDIALOG, pParent),
      m_strBtnText(lpszBtnText),
      m_strEditVal(lpszEditVal)
{
}

// OnInitDialog 中 UpdateData(FALSE) 套用

錯誤模式大全與修正

階段動作控制項視窗狀態
建構函數 CMyDialog()初始化 C++ 物件成員變數不存在 (m_hWnd = NULL)
DoModal() / Create()建立視窗、載入對話框模板、建立所有子控制項視窗、呼叫 OnInitDialog建立完成 (m_hWnd 有效)
OnInitDialog()視窗建立後、顯示前的初始化邏輯可安全操作
對話框運行中使用者互動可安全操作
OnDestroy / PostNcDestroy視窗銷毀失效 (m_hWnd 變 NULL)
錯誤代碼問題修正
dlg.m_btn.SetText(...); dlg.DoModal();建構後視窗未建立移至 OnInitDialog 或 Create 後
dlg.Create(...); dlg.m_btn.SetText(...); dlg.ShowWindow(SW_SHOW);正確 (非模態)無
dlg.DoModal(); dlg.m_btn.SetText(...);DoModal 返回後視窗已銷毀在 OnInitDialog 或對話框運行時 (訊息處理) 修改
全域變數傳遞資料耦合度高、執行緒不安全、難維護改用 Setter 函式、建構函數參數、DDX 資料成員

非模態對話框生命週期管理補充


// 正確的非模態對話框成員變數管理 (View/Frame 中)
class CMyView : public CView
{
    CMyDialog* m_pModelessDlg = nullptr; // 指標成員

    void OnShowModeless()
    {
        if (m_pModelessDlg) { m_pModelessDlg->SetFocus(); return; } // 已開啟則聚焦

        m_pModelessDlg = new CMyDialog(this);
        if (m_pModelessDlg->Create(IDD_MYDIALOG, this))
        {
            m_pModelessDlg->ShowWindow(SW_SHOW);
        }
        else
        {
            delete m_pModelessDlg;
            m_pModelessDlg = nullptr;
        }
    }

    // 視圖銷毀時確保關閉非模態對話框
    void OnDestroy()
    {
        if (m_pModelessDlg)
        {
            m_pModelessDlg->DestroyWindow(); // 觸發 PostNcDestroy
            m_pModelessDlg = nullptr;
        }
        CView::OnDestroy();
    }
};

// MyDialog.cpp - 自動刪除機制
void CMyDialog::PostNcDestroy()
{
    CDialog::PostNcDestroy();
    delete this; // 釋放 new 配置的記憶體
}

結語

MFC 對話框控制項操作的鐵律:**視窗建立完成後 (OnInitDialog / Create 返回 TRUE 後) 才能操作**。掌握 OnInitDialog (模態/非模態通用)、Create() 後 (非模態專用)、DDX/DDV 資料成員 (解耦合) 三大正確時機,配合 PostNcDestroy 自動釋放非模態對話框記憶體,即可避開 99% 的 ASSERT(IsWindow) 坑點。拒絕全域變數傳遞資料,擁抱封裝與 RAII。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved