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

MFC 有模式對話框:DoModal 詳解與回傳值處理

     編輯:關於C語言

有模式對話框特性回顧

  • 阻塞呼叫執行緒:DoModal() 直到對話框關閉才返回。
  • 停用父視窗:使用者無法操作同應用程式的其他視窗。
  • 適合簡單、短暫的互動:設定對話框、確認對話框、檔案開啟/儲存等。
  • 物件可置於棧上:函式結束自動銷毀,無記憶體洩漏風險。

最小化建立代碼

void CYourView::OnOpenModalDlg()
{
    CYourDlg dlg;                 // 棧上建立
    INT_PTR nResult = dlg.DoModal();  // 進入訊息迴圈,阻塞此處

    // 只有使用者關閉對話框後才會執行到這裡
    switch (nResult)
    {
    case IDOK:
        // 使用者按「確定」或呼叫 OnOK()
        TRACE(_T("使用者確認\n"));
        break;
    case IDCANCEL:
        // 使用者按「取消」、Esc 鍵、關閉按鈕或呼叫 OnCancel()
        TRACE(_T("使用者取消\n"));
        break;
    case -1:
        // DoModal 內部錯誤(資源不足、模板找不到等)
        AfxMessageBox(_T("對話框建立失敗"));
        break;
    default:
        // 自訂回傳值(呼叫 EndDialog(nCustomValue))
        TRACE(_T("自訂返回值: %d\n"), nResult);
        break;
    }
}

DoModal() 回傳值詳細說明

回傳值來源典型用途
IDOK (1)使用者按下預設按鈕(IDOK)、呼叫 OnOK()確認設定、儲存資料
IDCANCEL (2)使用者按 Esc、關閉視窗、呼叫 OnCancel()放棄變更
-1DoModal() 內部失敗錯誤處理
自訂整數呼叫 EndDialog(nResult)多按鈕對話框區分動作(如「另存新檔」、「覆蓋」、「取消」)

自訂關閉與回傳值:EndDialog()

// 對話框類別內部
void CTestDlg::OnBtnSaveAndClose()
{
    // 驗證資料...
    if (!UpdateData(TRUE)) return;  // 驗證失敗不關閉

    EndDialog(ID_SAVE_AND_CLOSE);  // 自訂回傳值,需在 resource.h 定義
}

void CTestDlg::OnBtnDiscard()
{
    EndDialog(ID_DISCARD_CHANGES);
}

// 呼叫端處理
INT_PTR nRet = dlg.DoModal();
if (nRet == ID_SAVE_AND_CLOSE) { /* 儲存並關閉 */ }
else if (nRet == ID_DISCARD_CHANGES) { /* 捨棄 */ }

重寫 OnOK()/OnCancel() 的正確姿勢

void CTestDlg::OnOK()
{
    // 1. 先做資料驗證/存取
    if (!UpdateData(TRUE))  // 失敗:DDV 驗證未過,焦點已導向錯誤控件
        return;             // 【關鍵】直接返回,不呼叫基類,對話框保持開啟

    // 2. 額外業務邏輯檢查
    if (m_strPassword != m_strConfirmPwd)
    {
        AfxMessageBox(_T("兩次密碼不一致"));
        GetDlgItem(IDC_CONFIRM_PWD)->SetFocus();
        return;
    }

    // 3. 一切正常,呼叫基類關閉對話框(內部會呼叫 EndDialog(IDOK))
    CDialog::OnOK();
}

void CTestDlg::OnCancel()
{
    // 通常不需額外邏輯,直接呼叫基類
    // 若需確認「確定放棄?」可在這裡加詢問
    if (AfxMessageBox(_T("確定放棄變更?"), MB_YESNO) == IDYES)
        CDialog::OnCancel();
}

常見陷阱與除錯技巧

  • 在 OnInitDialog 呼叫 EndDialog:對話框一閃即關,DoModal 返回對應值。可用於「無權限直接關閉」等場景。
  • 巢狀 DoModal:在有模式對話框中再開啟另一個有模式對話框完全合法(如檔案開啟對話框),內層關閉後外層繼續運行。
  • 定時器中開啟有模式對話框:極度危險!若上一個對話框未關閉,定時器再次觸發會疊加無數對話框 → 系統資源耗盡。改用無模式或單一實例模式。
  • 跨執行緒呼叫 DoModal:MFC UI 物件具有執行緒親和性,必須在建立該物件的執行緒呼叫。
  • 資源 ID 拼寫錯誤:DoModal 返回 -1,TRACE 輸出「Dialog template not found」。

進階:資料交換與驗證整合

// 對話框類
class CDataDlg : public CDialog
{
    CString m_strName;
    int     m_nAge;
    // ...
    virtual void DoDataExchange(CDataExchange* pDX);
};

void CDataDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_EDIT_NAME, m_strName);
    DDV_MaxChars(pDX, m_strName, 20);
    DDX_Text(pDX, IDC_EDIT_AGE, m_nAge);
    DDV_MinMaxInt(pDX, m_nAge, 1, 120);
}

// 呼叫端
void CMainView::OnEditData()
{
    CDataDlg dlg;
    dlg.m_strName = m_currentName;  // 傳入初始值
    dlg.m_nAge    = m_currentAge;

    if (dlg.DoModal() == IDOK)
    {
        // 使用者按確定且驗證通過,取回資料
        m_currentName = dlg.m_strName;
        m_currentAge  = dlg.m_nAge;
        UpdateViews();  // 刷新顯示
    }
    // 若取消,dlg 解構,成員變量銷毀,原資料不變
}

效能提示

有模式對話框建立/銷毀開銷較大(載入資源、建立視窗、訊息迴圈)。對於頻繁彈出的小型對話框(如工具提示、顏色選擇器),考慮:

  • 改用無模式單一實例
  • 使用 CPropertySheet 頁籤整合多個子對話框
  • 預先建立對話框物件,反覆呼叫 DoModal(需手動重置資料)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved