IDD_CALCULATOR_DIALOG:
IDC_DISPLAY,屬性:Read-only、Right-align、數字字體,顯示輸入與結果。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) 雙向同步。
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; // 小數點後位數(用於小數累加計算)
// ...
};
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);
}
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 模式分離視圖與邏輯。 |
UpdateData(TRUE) 讀取控制項→變量,UpdateData(FALSE) 寫入變量→控制項。邏輯內部直接操作狀態變量再 SetDlgItemText 更可控。%g 自動選擇定點/科學計數法並去除尾隨零;定點需用 %.2f 等。m_hasDecimal 防禦。ON_CONTROL_RANGE 減少映射條目。這個 MFC 計算器專案雖小,却完整涵蓋了 Windows 桌面開發的核心環節:資源設計、訊息映射、DDX/DDV、狀態機建模、邊界條件處理。對於 C++ 初學者,它是從語法入門走向工程實踐的絕佳樣本。建議在掌握基礎版後,嘗試上述擴展方向,逐步建立「領域邏輯與 UI 解耦」、「可測試性設計」、「現代 C++ 風格重構」的工程化思維。