news 2026/4/28 1:44:05

使用WinForm仿制Win10计算器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用WinForm仿制Win10计算器

项目概述

在日常生活和程序开发中,计算器作为一个基础但功能强大的工具,其设计与实现能全面展示GUI编程的核心概念。本项目基于C# WinForm技术,完整仿制Windows 10系统中的计算器应用,实现了标准模式、科学模式以及程序员模式三大核心功能模块,支持键盘与鼠标双操作模式,具备完整的编辑功能和界面切换能力。

📊 技术架构与设计模式

项目结构

设计模式应用

  • 状态模式:管理计算器的不同运算状态

  • 策略模式:实现不同计算算法的灵活切换

🎯 功能实现详解

1. 核心计算引擎实现

// --- 核心逻辑 5:等号 (=) --- private void btn_Result_Click(object sender, EventArgs e) { if (CheckErrorState()) return; if (!string.IsNullOrEmpty(_operationClicked)) { double secondOperand; double.TryParse(textBox1.Text, out secondOperand); // 如果有单目/百分比显示的特殊字符串,优先用它拼表达式,否则用数字 string exprPart = string.IsNullOrEmpty(_currentExpressionPart) ? secondOperand.ToString() : _currentExpressionPart; string fullExpr = $"{_resultValue} {_operationClicked} {exprPart} ="; double finalRes = CalculateMath(_resultValue, secondOperand, _operationClicked); if (double.IsNaN(finalRes)) { ShowError("除数不能为零"); return; } textBox1.Text = finalRes.ToString(); textBox2.Text = fullExpr; AddToHistory(fullExpr, finalRes.ToString()); _resultValue = 0; _operationClicked = ""; _isNewEntry = true; _isCalculationPerformed = true; _currentExpressionPart = ""; } // 单目运算按等号也进历史 else if (!string.IsNullOrEmpty(_currentExpressionPart)) { string fullExpr = $"{_currentExpressionPart} ="; string res = textBox1.Text; textBox2.Text = fullExpr; AddToHistory(fullExpr, res); _isNewEntry = true; _isCalculationPerformed = true; _currentExpressionPart = ""; } }

2. 界面设计

1.标准模式界面设计

计算器标准模式

2.科学模式界面设计

计算器科学模式

3.程序员模式界面设计

计算器程序员模式

3. 键盘事件处理

// --- 键盘支持 (实现 ProcessCmdKey 以处理所有按键) --- protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { // --- 处理主键盘的组合键 (Shift + 8 为乘号 *) --- // 注意:必须放在 switch 之前判断,因为 switch 很难直接匹配组合键 if (keyData == (Keys.Shift | Keys.D8)) { btn_mult.PerformClick(); return true; } // 数字键 (主键盘 + 小键盘) if ((keyData >= Keys.D0 && keyData <= Keys.D9) || (keyData >= Keys.NumPad0 && keyData <= Keys.NumPad9)) { // 排除 Shift+数字 的情况(防止冲突,比如 Shift+8 是乘号,不应输入数字8) // 如果按下了 Shift 且按的是主键盘数字,则跳过数字输入逻辑(交由上面的组合键或忽略) if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift && keyData <= Keys.D9) { // 这里不做处理,让它往下走,或者直接 break } else { string numStr = keyData.ToString().Replace("NumPad", "").Replace("D", ""); SimulateNumInput(numStr); return true; } } // 运算符与功能键 switch (keyData) { // 加号 (小键盘 + 或 主键盘 Shift + =) case Keys.Add: case (Keys.Shift | Keys.Oemplus): btn_add.PerformClick(); return true; // 减号 (小键盘 - 或 主键盘 -) case Keys.Subtract: case Keys.OemMinus: btn_sub.PerformClick(); return true; // 乘号 (小键盘 *) case Keys.Multiply: btn_mult.PerformClick(); return true; // 除号 (小键盘 / 或 主键盘 /) case Keys.Divide: case Keys.OemQuestion: btn_div.PerformClick(); return true; // 回车键 (=) case Keys.Enter: btn_equal.PerformClick(); return true; // 退格键 (BackSpace) case Keys.Back: btn_back.PerformClick(); return true; // ESC键 (C) case Keys.Escape: btn_clear2.PerformClick(); return true; // 小数点 (小键盘 . 或 主键盘 .) case Keys.Decimal: case Keys.OemPeriod: btn_point.PerformClick(); return true; } return base.ProcessCmdKey(ref msg, keyData); } private void SimulateNumInput(string num) { // 创建一个临时按钮触发点击逻辑,复用现有代码 Button temp = new Button() { Text = num }; btn_Number_Click(temp, EventArgs.Empty); }

4. 科学计算功能实现

科学计算函数
// --- 2. 双目运算符 (+ - * /) --- private void btn_Operator_Click(object sender, EventArgs e) { if (CheckErrorState()) return; Button btn = (Button)sender; string op = btn.Text; if (op == "×") op = "×"; if (op == "÷") op = "÷"; if (btn == Btxdeycifang) op = "^"; if (btn == Btmod) op = "Mod"; // 如果刚按过运算符,只替换运算符 if (_isNewEntry && _expressionTokens.Count > 0 && IsOperator(_expressionTokens.Last()) && string.IsNullOrEmpty(_currentExpressionPart)) { _expressionTokens[_expressionTokens.Count - 1] = op; UpdateDisplay(false); return; } // 1. 将当前数值(或单目表达式)推入列表 PushCurrentValueToTokens(); // 2. 推入运算符 _expressionTokens.Add(op); _currentExpressionPart = ""; // 3. 连续计算预览 // 计算当前所有 Token 的结果并显示在 T1,但不清除 Token 列表 double previewRes = MathEvaluator.Evaluate(_expressionTokens, true); // true = 忽略末尾运算符 if (!double.IsNaN(previewRes)) { textBox1.Text = previewRes.ToString(); } UpdateDisplay(false); _isNewEntry = true; _isResultShowing = false; } // --- 3. 单目运算符 (立即包裹) --- private void btn_Unary_Click(object sender, EventArgs e) { if (CheckErrorState()) return; Button btn = (Button)sender; if (!double.TryParse(textBox1.Text, out double num)) return; double res = 0; string func = btn.Text.ToLower(); string innerContent; if (!string.IsNullOrEmpty(_currentExpressionPart)) { innerContent = _currentExpressionPart; } else { innerContent = num.ToString(); } // 根据按钮识别功能 if (btn == Btln || func == "ln") { if (num <= 0) { ShowError("无效输入"); return; } res = Math.Log(num); } else if (btn == Btlog || func == "log") { if (num <= 0) { ShowError("无效输入"); return; } res = Math.Log10(num); } else if (btn == Btjiecheng || func.Contains("!")) { if (num < 0 || Math.Floor(num) != num) { ShowError("无效输入"); return; } res = Factorial((int)num); } else if (btn == Btkaipingfang || func.Contains("√")) { if (num < 0) { ShowError("无效输入"); return; } res = Math.Sqrt(num); } else if (btn == Btsquare || func.Contains("x^2")) { res = Math.Pow(num, 2); } else if (btn == Btdaoshu || func.Contains("1/x")) { if (num == 0) { ShowError("除数不能为零"); return; } res = 1.0 / num; } else if (btn == Bt10decifang || func.Contains("10^")) { res = Math.Pow(10, num); } else if (btn == Btjueduizhi || func.Contains("abs")) { res = Math.Abs(num); } // 三角函数兼容 if (func.Contains("sin")) { res = Math.Sin(ToRad(num)); } if (func.Contains("cos")) { res = Math.Cos(ToRad(num)); } if (func.Contains("tan")) { res = Math.Tan(ToRad(num)); } string symbol = ""; if (btn == Btln || func == "ln") symbol = $"ln({innerContent})"; else if (btn == Btlog || func == "log") symbol = $"log({innerContent})"; else if (btn == Btjiecheng || func.Contains("!")) symbol = $"fact({innerContent})"; else if (btn == Btkaipingfang || func.Contains("√")) symbol = $"√({innerContent})"; else if (btn == Btsquare || func.Contains("x^2")) symbol = $"sqr({innerContent})"; else if (btn == Btdaoshu || func.Contains("1/x")) symbol = $"1/({innerContent})"; else if (btn == Bt10decifang || func.Contains("10^")) symbol = $"10^({innerContent})"; else if (btn == Btjueduizhi || func.Contains("abs")) symbol = $"abs({innerContent})"; else if (func.Contains("sin")) symbol = $"sin({innerContent})"; else if (func.Contains("cos")) symbol = $"cos({innerContent})"; else if (func.Contains("tan")) symbol = $"tan({innerContent})"; // 核心逻辑: 立即更新 T1 结果,记录 T2 表达式部分 textBox1.Text = res.ToString(); _currentExpressionPart = symbol; // 更新上方 T2 显示 (不按等号) UpdateDisplay(false); _isNewEntry = true; }

5. 右键菜单与编辑功能

private void InitializeContextMenu() { // 简单的右键复制粘贴 ContextMenuStrip ctx = new ContextMenuStrip(); ctx.Items.Add("复制", null, (s, e) => Clipboard.SetText(textBox1.Text)); ctx.Items.Add("粘贴", null, (s, e) => { if (Clipboard.ContainsText() && double.TryParse(Clipboard.GetText(), out _)) { textBox1.Text = Clipboard.GetText(); } }); textBox1.ContextMenuStrip = ctx; }

🔧 核心代码实现

主窗体实现

public Form1() { InitializeComponent(); this.StartPosition = FormStartPosition.CenterScreen; this.KeyPreview = true; // 允许窗体捕获键盘 //设定所有button按钮属性 SetAllButton(); InitializeCalculator(); InitializeContextMenu(); this.FormClosed += (s, e) => Application.Exit(); }

计算器数字输入

// --- 核心逻辑 1:数字输入 --- private void btn_Number_Click(object sender, EventArgs e) { if (CheckErrorState()) return; Button btn = (Button)sender; // 如果刚做完单目运算(如sqrt),又直接输数字,需要重置表达式部分 if (!string.IsNullOrEmpty(_currentExpressionPart)) { _currentExpressionPart = ""; // 如果没有运算符,说明是全新开始 if (string.IsNullOrEmpty(_operationClicked)) { textBox2.Text = ""; } else { textBox2.Text = $"{_resultValue} {_operationClicked}"; } } if (textBox1.Text == "0" || _isNewEntry) { textBox1.Text = btn.Text == "." ? "0." : btn.Text; _isNewEntry = false; } else { if (btn.Text == "." && textBox1.Text.Contains(".")) return; textBox1.Text += btn.Text; } // 如果刚运算完(=)就输数字,彻底重置 if (_isCalculationPerformed) { textBox2.Text = ""; _resultValue = 0; _operationClicked = ""; _isCalculationPerformed = false; } }

🎨 界面美化技巧

1. Win10风格按钮设计

private void SetAllButton() { List<Control> buttonList = new List<Control>(); foreach (Control con in Controls) { //判断控件类型是否为按钮 if (con is Button) { buttonList.Add(con); } } //遍历所有List,设定属性 for (int i = 0; i < buttonList.Count; i++) { Button btn = buttonList[i] as Button; btn.Fillet(0.3); } }

🚀 高级特性实现

1. 计算历史记录

// --- 历史记录系统 --- private void AddToHistory(string expr, string res) { _historyList.Add(new CalcHistory { Expr = expr, Res = res }); RenderHistoryPanel(); } private void RenderHistoryPanel() { historyPanel.Controls.Clear(); // 倒序遍历,最新的在上面 for (int i = _historyList.Count - 1; i >= 0; i--) { var item = _historyList[i]; Panel p = new Panel { Size = new Size(330, 60), Dock = DockStyle.Top, Padding = new Padding(0, 0, 0, 5) }; p.BackColor = Color.Transparent; Label lblExpr = new Label { Text = item.Expr, Location = new Point(10, 5), AutoSize = true, ForeColor = Color.Gray }; Label lblRes = new Label { Text = item.Res, Location = new Point(10, 28), Font = new Font("Microsoft YaHei", 14, FontStyle.Bold), AutoSize = true }; // 点击单条记录填入输入框 EventHandler clickHandler = (s, e) => { textBox1.Text = item.Res; textBox2.Text = item.Expr; _isNewEntry = true; _isCalculationPerformed = true; _operationClicked = ""; _resultValue = 0; }; p.Click += clickHandler; lblExpr.Click += clickHandler; lblRes.Click += clickHandler; // 鼠标悬停效果 p.MouseEnter += (s, e) => p.BackColor = Color.LightGray; p.MouseLeave += (s, e) => p.BackColor = Color.Transparent; p.Controls.Add(lblRes); p.Controls.Add(lblExpr); historyPanel.Controls.Add(p); } } private void button1_Click(object sender, EventArgs e) // 垃圾桶清除历史 { _historyList.Clear(); RenderHistoryPanel(); } // --- 记忆系统 (Memory) 深度重构 --- // 主界面的 MS: 添加新记忆 (不覆盖,添加到栈顶) private void btn_MS_Click(object sender, EventArgs e) { if (CheckErrorState()) return; double val; if (double.TryParse(textBox1.Text, out val)) { _memoryList.Insert(0, val); // 插到最前面 RenderMemoryPanel(); _isNewEntry = true; } } // 主界面的 M+: 给最近的一条记忆加值 private void btn_M_Main_Add_Click(object sender, EventArgs e) { if (CheckErrorState()) return; if (_memoryList.Count == 0) { btn_MS_Click(sender, e); return; } double val; if (double.TryParse(textBox1.Text, out val)) { _memoryList[0] += val; RenderMemoryPanel(); _isNewEntry = true; } } // 主界面的 M-: 给最近的一条记忆减值 private void btn_M_Main_Sub_Click(object sender, EventArgs e) { if (CheckErrorState()) return; if (_memoryList.Count == 0) { btn_MS_Click(sender, e); return; } // Win10如果没记忆按M-也会存新 double val; if (double.TryParse(textBox1.Text, out val)) { _memoryList[0] -= val; RenderMemoryPanel(); _isNewEntry = true; } } // 渲染记忆面板,每条带三个按钮 private void RenderMemoryPanel() { memoryPanel.Controls.Clear(); for (int i = _memoryList.Count - 1; i >= 0; i--) { int index = i; // 闭包陷阱,存局部变量 double memVal = _memoryList[index]; Panel p = new Panel { Size = new Size(330, 75), Dock = DockStyle.Top }; Label lblVal = new Label { Text = memVal.ToString(), Font = new Font("Microsoft YaHei", 16, FontStyle.Bold), Location = new Point(10, 5), AutoSize = true }; // 点击数值填入 lblVal.Click += (s, e) => { textBox1.Text = memVal.ToString(); _isNewEntry = true; }; // 创建三个小按钮 Button btnMC = CreateMemButton("MC", 10, 40); Button btnMAdd = CreateMemButton("M+", 70, 40); Button btnMSub = CreateMemButton("M-", 130, 40); // --- 按钮逻辑 --- btnMC.Click += (s, e) => { _memoryList.RemoveAt(index); RenderMemoryPanel(); }; btnMAdd.Click += (s, e) => { double currentInput; if (double.TryParse(textBox1.Text, out currentInput)) { _memoryList[index] += currentInput; RenderMemoryPanel(); // 刷新显示 _isNewEntry = true; } }; btnMSub.Click += (s, e) => { double currentInput; if (double.TryParse(textBox1.Text, out currentInput)) { _memoryList[index] -= currentInput; RenderMemoryPanel(); _isNewEntry = true; } }; p.Controls.Add(lblVal); p.Controls.Add(btnMC); p.Controls.Add(btnMAdd); p.Controls.Add(btnMSub); p.MouseEnter += (s, e) => p.BackColor = Color.LightGray; p.MouseLeave += (s, e) => p.BackColor = Color.WhiteSmoke; memoryPanel.Controls.Add(p); } }

📱 部署与发布

1. 发布设置

  • 目标框架:.NET Framework 4.8

  • 输出类型:Windows应用程序

  • 生成单文件:可选

  • 生成可移植版本:支持

2. 安装程序制作

使用Visual Studio Installer Projects或Inno Setup创建安装程序

🎯 扩展功能建议

  1. 程序员模式:添加位运算、逻辑运算

  2. 单位换算:长度、重量、温度等

  3. 汇率转换:实时获取汇率数据

  4. 历史图表:可视化计算历史

  5. 自定义主题:支持用户自定义界面颜色

  6. 语音输入:通过语音识别进行输入

  7. 多语言支持:国际化界面

  8. 云端同步:保存计算历史和设置

📋 项目总结

通过本项目的实现,我们不仅完成了一个功能完整的计算器应用,还深入掌握了:

  1. WinForm高级编程:自定义绘制、事件处理、控件布局

  2. 计算器核心算法:表达式求值、状态管理、错误处理

  3. 用户体验优化:键盘支持、右键菜单、界面美化

  4. 工程化实践:代码分层、设计模式、单元测试

本项目可作为学习WinForm编程的优秀示例,涵盖了GUI开发的各个方面,适合初学者系统学习和进阶开发者参考借鉴。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 1:43:22

软件事件管理化的异常处理与恢复

软件事件管理化的异常处理与恢复&#xff1a;构建稳定系统的关键 在数字化时代&#xff0c;软件系统的稳定性直接影响用户体验和企业运营。异常事件难以避免&#xff0c;如何高效管理并快速恢复成为技术团队的核心挑战。软件事件管理化的异常处理与恢复&#xff0c;通过系统化…

作者头像 李华
网站建设 2026/4/28 1:41:20

八大网盘直链解析技术深度解析:开源工具LinkSwift实现原理与实践指南

八大网盘直链解析技术深度解析&#xff1a;开源工具LinkSwift实现原理与实践指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移…

作者头像 李华
网站建设 2026/4/28 1:37:00

Three.JS结合AI工具快速开发3D游戏原型

1. 项目概述"One Shot 3D Games You Can Test Immediately using Three.JS Grok and Claude"这个标题揭示了三个关键要素&#xff1a;快速创建3D游戏、即时测试能力&#xff0c;以及Three.JS、Grok和Claude这三个技术栈的组合使用。作为一名长期从事Web 3D开发的工程…

作者头像 李华
网站建设 2026/4/28 1:36:33

SPI NOR闪存技术解析与嵌入式系统优化实践

1. SPI NOR闪存技术概述在嵌入式系统设计中&#xff0c;存储器的选择往往需要在性能、成本和复杂度之间寻找平衡点。SPI NOR闪存凭借其独特的优势&#xff0c;已经成为众多嵌入式应用的首选非易失性存储解决方案。作为一名长期从事嵌入式系统开发的工程师&#xff0c;我见证了S…

作者头像 李华
网站建设 2026/4/28 1:27:19

05.实战 YOLOv8:零错误端到端目标检测教程

YOLO(You Only Look Once)是目前工业界应用最广泛的目标检测算法之一。本文以YOLOv8为基准,从原理到实战,完整覆盖目标检测全流程。内容涵盖模型结构解析、数据集构建、训练调优、推理部署以及常见陷阱。所有代码基于Ultralytics官方库,保证零错误可运行。读者完成本文后,…

作者头像 李华