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

MFC 計算器開發實戰:從對話框設計到事件驅動邏輯實現

     編輯:關於C++

專案背景與技術棧

  • 目標:構建一個功能完整的標準四則運算計算器(支援小數、連續運算、清除)。
  • 框架:MFC (Microsoft Foundation Classes) 對話框應用程式。
  • 開發環境:Visual Studio (VC++),資源編輯器設計 UI,類向導綁定控制項與事件。
  • 核心邏輯:有限狀態機模擬輸入流程(操作數輸入 → 運算符 → 操作數輸入 → 等號/下一運算符)。

介面設計:資源編輯器佈局

  1. 建立 MFC 對話框專案(專案類型:MFC Application → Dialog based)。
  2. 在資源視圖編輯 IDD_CALCULATOR_DIALOG:
    • 編輯控制項 (Edit Control):ID IDC_DISPLAY,屬性:Read-only、Right-align、數字字體,顯示輸入與結果。
    • 按鈕控制項:0-9 數字鍵 (IDC_BTN_0~IDC_BTN_9)、小數點 (IDC_BTN_DOT)、運算符 + - × ÷ (IDC_BTN_ADD 等)、等號 (IDC_BTN_EQ)、清除 (IDC_BTN_CE)。

變量綁定與狀態管理

控制項變量

使用類向導為編輯框添加 CString 或 double 類型變量 m_strDisplay / m_dDisplay,實現 DDX (Dialog Data Exchange) 雙向同步。

核心狀態變量 (在 Dialog 類中聲明)


class CCalculatorDlg : public CDialogEx {
    // 運算狀態
    double m_operandA = 0.0;      // 第一操作數
    double m_operandB = 0.0;      // 第二操作數
    int    m_operator = 0;        // 當前運算符:0=無, 1=+, 2=-, 3=*, 4=/
    int    m_inputStage = 0;      // 輸入階段:0=輸入A, 1=輸入B
    bool   m_hasDecimal = false;  // 是否已輸入小數點
    int    m_decimalPlaces = 0;   // 小數點後位數(用於小數累加計算)
    // ...
};

關鍵事件處理邏輯實現

數字鍵 0-9 統一處理


void CCalculatorDlg::OnBtnDigit(UINT nID) {
    int digit = nID - IDC_BTN_0; // 0-9
    UpdateData(TRUE); // 從控制項讀取顯示值(可選,邏輯內部維護更佳)

    double* currentOperand = (m_inputStage == 0) ? &m_operandA : &m_operandB;

    if (!m_hasDecimal) {
        *currentOperand = *currentOperand * 10 + digit;
    } else {
        // 小數累加:digit / 10^places
        double factor = 1.0;
        for (int i = 0; i <= m_decimalPlaces; ++i) factor *= 10.0;
        *currentOperand += digit / factor;
        ++m_decimalPlaces;
    }

    // 更新顯示
    CString str;
    str.Format(_T("%g"), *currentOperand); // %g 自動去除尾隨零
    SetDlgItemText(IDC_DISPLAY, str);
}

技巧:在資源編輯器將 10 個數字按鈕的 Command ID 設為連續 IDC_BTN_0~IDC_BTN_9,類向導添加 ON_CONTROL_RANGE(BN_CLICKED, IDC_BTN_0, IDC_BTN_9, OnBtnDigit) 統一映射,避免 10 個重複函數。

小數點按鈕


void CCalculatorDlg::OnBtnDot() {
    if (!m_hasDecimal) {
        m_hasDecimal = true;
        m_decimalPlaces = 0;
        // 顯示補全 "0." 或 "123."
        CString str;
        double* op = (m_inputStage == 0) ? &m_operandA : &m_operandB;
        str.Format(_T("%g."), *op);
        SetDlgItemText(IDC_DISPLAY, str);
    }
}

運算符按鈕 (+ - × ÷)


void CCalculatorDlg::OnBtnOperator(UINT nID) {
    // 如果已有運算符且正在輸入 B,先執行上一運算(連續運算支援)
    if (m_operator != 0 && m_inputStage == 1) {
        OnBtnEqual(); // 計算 A op B → 結果存入 A
    }

    m_operator = nID - IDC_BTN_ADD + 1; // 1=+, 2=-, 3=*, 4=/
    m_inputStage = 1; // 切換輸入 B
    m_hasDecimal = false;
    m_decimalPlaces = 0;
    m_operandB = 0.0;
}

等號按鈕:執行計算


void CCalculatorDlg::OnBtnEqual() {
    if (m_operator == 0 || m_inputStage != 1) return; // 無運算符或未輸入 B

    double result = 0.0;
    switch (m_operator) {
        case 1: result = m_operandA + m_operandB; break;
        case 2: result = m_operandA - m_operandB; break;
        case 3: result = m_operandA * m_operandB; break;
        case 4: 
            if (m_operandB == 0.0) { // 除零保護
                AfxMessageBox(_T("除數不能為零"));
                return;
            }
            result = m_operandA / m_operandB; 
            break;
    }

    m_operandA = result; // 結果作為下一輪的 A
    m_operator = 0;      // 清除運算符
    m_inputStage = 0;    // 重置輸入階段
    m_hasDecimal = false;
    m_decimalPlaces = 0;

    CString str;
    str.Format(_T("%g"), result);
    SetDlgItemText(IDC_DISPLAY, str);
}

清除按鈕 (CE/C)


void CCalculatorDlg::OnBtnClear() {
    m_operandA = m_operandB = 0.0;
    m_operator = 0;
    m_inputStage = 0;
    m_hasDecimal = false;
    m_decimalPlaces = 0;
    SetDlgItemText(IDC_DISPLAY, _T("0"));
}

初始化與收尾


BOOL CCalculatorDlg::OnInitDialog() {
    CDialogEx::OnInitDialog();
    // 圖標、字體等初始化
    SetDlgItemText(IDC_DISPLAY, _T("0")); // 初始顯示 0
    return TRUE;
}

進階優化與擴展方向

方向說明
鍵盤支援重寫 PreTranslateMessage 捕獲 WM_KEYDOWN,映射數字鍵/運算符/Enter/Backspace/Escape。
科學計算模式擴展運算符枚舉,添加 sin/cos/log/^/√ 等單目/雙目運算,引入運算符優先級解析(中綴轉後綴/遞歸下降)。
歷史記錄用 CListBox/CListCtrl 記錄每次運算式與結果,支援雙擊回填。
高精度/大數引入 boost::multiprecision::cpp_dec_float 或自定義 BCD 類,解決 double 精度缺失(如 0.1+0.2≠0.3)。
單元測試將計算核心邏輯提取為獨立 CalculatorEngine 類,配合 Google Test 驗證邊界條件。
現代化 UI遷移至 WPF/WinUI 3 或 Qt,使用 MVVM 模式分離視圖與邏輯。

常見坑點總結

  1. DDX 同步時機:UpdateData(TRUE) 讀取控制項→變量,UpdateData(FALSE) 寫入變量→控制項。邏輯內部直接操作狀態變量再 SetDlgItemText 更可控。
  2. 浮點數顯示格式:%g 自動選擇定點/科學計數法並去除尾隨零;定點需用 %.2f 等。
  3. 連續運算邏輯:輸入運算符時若已有未計算的運算,應先執行(如 1 + 2 × 3 按 × 時先算 1+2=3,再準備 3×3)。
  4. 小數點重複輸入:m_hasDecimal 防禦。
  5. 除零異常:必須攔截並友好提示,避免崩潰。
  6. 資源 ID 衝突:統一命名規範,使用 ON_CONTROL_RANGE 減少映射條目。

結語

這個 MFC 計算器專案雖小,却完整涵蓋了 Windows 桌面開發的核心環節:資源設計、訊息映射、DDX/DDV、狀態機建模、邊界條件處理。對於 C++ 初學者,它是從語法入門走向工程實踐的絕佳樣本。建議在掌握基礎版後,嘗試上述擴展方向,逐步建立「領域邏輯與 UI 解耦」、「可測試性設計」、「現代 C++ 風格重構」的工程化思維。

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