建立一個標準四則運算計算機是學習 WinForms 事件驅動程式設計、狀態機制與 UI 邏輯分離的經典入門專案。本文完整提供介面配置、變數設計、按鈕事件處理、運算邏輯與完整可執行程式碼。
| 控制項 | Name | Text | 關鍵屬性 |
|---|---|---|---|
| Form | calcForm | 計算機 | FormBorderStyle=FixedDialog, MaximizeBox=False |
| TextBox | txtCalc | (空) | ReadOnly=True, RightToLeft=Yes, Font=Consolas 14pt |
| Button | btn0~btn9 | 0~9 | Size=32x32, Font=12pt |
| Button | btnDot | . | |
| Button | btnAdd | + | BackColor=Control, Height=80 (跨兩行) |
| Button | btnSub | - | |
| Button | btnMul | × | Text="*" |
| Button | btnDiv | ÷ | Text="/" |
| Button | btnEqu | = | Width=80, BackColor=Control |
| Button | btnClr | AC | BackColor=Control |
public partial class CalcForm : Form
{
private double _accumulator = 0; // 運算數 A (累積結果)
private double _current = 0; // 運算數 B (目前輸入)
private string _pendingOp = "="; // 待執行運算子
private bool _clearOnNextInput = true; // 是否清空畫面準備新輸入
private bool _firstOperand = true; // 是否為第一個運算數
public CalcForm() => InitializeComponent();
}
private void NumberButton_Click(object sender, EventArgs e)
{
if (_clearOnNextInput)
{
txtCalc.Clear();
_clearOnNextInput = false;
}
Button btn = (Button)sender;
string input = btn.Text;
// 處理小數點:避免重複、開頭補 0
if (input == ".")
{
if (txtCalc.Text.Contains(".")) return;
if (string.IsNullOrEmpty(txtCalc.Text)) txtCalc.Text = "0";
}
txtCalc.Text += input;
_current = double.Parse(txtCalc.Text);
}
設計器中將 btn0~btn9、btnDot 的 Click 事件全指向此方法。
private void OperatorButton_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
string newOp = btn.Text; // "+", "-", "*", "/"
if (!_firstOperand)
{
Calculate(); // 執行前一個待定運算
}
else
{
_accumulator = _current;
_firstOperand = false;
}
_pendingOp = newOp;
_clearOnNextInput = true; // 下一個數字輸入時清空畫面
}
private void btnEqu_Click(object sender, EventArgs e)
{
Calculate();
_pendingOp = "=";
_firstOperand = true; // 重置狀態,準備新計算
}
private void Calculate()
{
switch (_pendingOp)
{
case "+": _accumulator += _current; break;
case "-": _accumulator -= _current; break;
case "*": _accumulator *= _current; break;
case "/":
if (_current == 0)
{
txtCalc.Text = "Error: Div by 0";
_accumulator = 0;
_firstOperand = true;
return;
}
_accumulator /= _current;
break;
case "=": // 重複等號:再次執行相同運算
break;
}
txtCalc.Text = _accumulator.ToString("G15"); // 避免科學記號,保留精度
_current = _accumulator; // 為連續運算準備
}
private void btnClr_Click(object sender, EventArgs e)
{
_accumulator = 0;
_current = 0;
_pendingOp = "=";
_firstOperand = true;
_clearOnNextInput = true;
txtCalc.Clear();
txtCalc.Focus();
}
using System;
using System.Windows.Forms;
namespace WinCalc
{
public partial class CalcForm : Form
{
private double _acc = 0, _cur = 0;
private string _op = "=";
private bool _clearNext = true, _first = true;
public CalcForm() { InitializeComponent(); }
private void Num_Click(object s, EventArgs e)
{
if (_clearNext) { txtCalc.Clear(); _clearNext = false; }
var b = (Button)s;
if (b.Text == ".") { if (txtCalc.Text.Contains(".")) return; if (txtCalc.Text == "") txtCalc.Text = "0"; }
txtCalc.Text += b.Text;
_cur = double.Parse(txtCalc.Text);
}
private void Op_Click(object s, EventArgs e)
{
var b = (Button)s;
if (!_first) Calc(); else _acc = _cur;
_op = b.Text; _first = false; _clearNext = true;
}
private void Eq_Click(object s, EventArgs e) { Calc(); _op = "="; _first = true; }
private void Calc()
{
switch (_op)
{
case "+": _acc += _cur; break;
case "-": _acc -= _cur; break;
case "*": _acc *= _cur; break;
case "/": if (_cur == 0) { txtCalc.Text = "Error"; _acc = 0; _first = true; return; } _acc /= _cur; break;
}
txtCalc.Text = _acc.ToString("G15");
_cur = _acc;
}
private void Clr_Click(object s, EventArgs e)
{
_acc = _cur = 0; _op = "="; _first = true; _clearNext = true;
txtCalc.Clear(); txtCalc.Focus();
}
}
}
ProcessCmdKey 捕捉 Keys.NumPad0~Keys.Divide 觸發對應按鈕 PerformClick()。ListBox 記錄每次計算式與結果。sin, cos, sqrt, % 等單元運算子。double 浮點誤差,金融計算請用 decimal。