1. 项目概述
这个MFC计算器项目是我在高级程序设计课程中的第九次实验作业。作为一个刚接触MFC框架不久的开发者,我选择实现一个具备基本四则运算功能的计算器程序,主要目的是熟悉MFC对话框应用程序的开发流程,掌握按钮、编辑框等常用控件的使用方法。
计算器界面包含四个编辑框:第一个用于显示第一个操作数,第二个用于显示运算符,第三个用于显示第二个操作数,第四个则用于输出计算结果。此外还有数字按钮0-9、小数点按钮、加减乘除运算符按钮、等号按钮和清零按钮。整个界面布局简洁明了,操作逻辑与Windows系统自带的计算器类似。
提示:在MFC对话框应用中,控件的ID命名最好遵循一定的规范。比如编辑框可以用IDC_EDIT开头,按钮可以用IDC_BUTTON开头,这样在代码中更容易识别和维护。
2. 开发环境搭建
2.1 开发工具选择
我选择使用Visual Studio 2017作为开发环境,主要基于以下几点考虑:
- 这是学校实验室统一安装的版本,便于代码共享和问题讨论
- VS2017对MFC的支持已经非常成熟稳定
- 相比更新版本的VS,2017版对教学用途来说功能足够且运行更轻量
安装时需要注意勾选"使用C++的桌面开发"工作负载,确保包含MFC组件。如果遗漏了,可以在Visual Studio Installer中后期添加。
2.2 项目创建步骤
- 打开VS2017,选择"文件"->"新建"->"项目"
- 在模板中选择"MFC应用程序"
- 设置项目名称和位置,我将其命名为"SimpleCalculator"
- 在应用程序类型中选择"基于对话框"
- 取消勾选"使用Unicode库"(本实验对字符集要求不高)
- 保持其他选项默认,点击完成
创建完成后,VS会自动生成基本的对话框应用程序框架,包括:
- 一个主对话框资源
- 应用程序类(CSimpleCalculatorApp)
- 对话框类(CSimpleCalculatorDlg)
- 标准MFC支持文件
3. 界面设计与控件布局
3.1 对话框资源编辑
在资源视图中打开IDD_SIMPLECALCULATOR_DIALOG对话框资源,这是程序的主界面。我首先调整对话框大小至合适尺寸(约300x400像素),然后开始添加控件:
-
添加4个Edit Control编辑框,分别用于:
- IDC_EDIT1:第一个操作数
- IDC_EDIT2:运算符
- IDC_EDIT3:第二个操作数
- IDC_EDIT4:计算结果
-
添加16个Button按钮:
- 数字按钮0-9(IDC_BUTTON1-IDC_BUTTON10)
- 小数点按钮(IDC_BUTTON11)
- 清零按钮(IDC_BUTTON12)
- 加减乘除运算符按钮(IDC_BUTTON13-IDC_BUTTON16)
- 等号按钮(IDC_BUTTON17)
3.2 控件属性设置
为了使界面更美观,我对部分控件进行了属性调整:
-
编辑框:
- 设置Read-only属性为True,防止用户直接键盘输入
- 调整字体为Consolas,12pt,保证数字显示整齐
- 设置Align Text为Right,数字右对齐
-
按钮:
- 调整大小一致(约40x40像素)
- 设置字体为Arial,10pt
- 运算符按钮使用不同背景色突出显示
注意:在设置控件ID时,建议采用有意义的命名方式,如数字按钮可以命名为IDC_BTN_1、IDC_BTN_2等,而不是简单的IDC_BUTTON1、IDC_BUTTON2。这样在代码中更容易识别和维护。
4. 核心功能实现
4.1 变量与成员函数声明
在对话框类头文件(SimpleCalculatorDlg.h)中添加以下成员变量和函数:
cpp复制// 成员变量
CString str1; // 第一个操作数字符串
CString str2; // 运算符字符串
CString str3; // 第二个操作数字符串
CString str; // 结果字符串
double number1; // 第一个操作数
double number2; // 第二个操作数
double result; // 计算结果
// 成员函数
void input(char* a); // 数字输入处理函数
4.2 消息映射与事件处理
在对话框实现文件(SimpleCalculatorDlg.cpp)中,为每个按钮添加消息映射和点击事件处理函数:
cpp复制BEGIN_MESSAGE_MAP(CSimpleCalculatorDlg, CDialogEx)
ON_BN_CLICKED(IDC_BTN_0, &CSimpleCalculatorDlg::OnBnClickedBtn0)
ON_BN_CLICKED(IDC_BTN_1, &CSimpleCalculatorDlg::OnBnClickedBtn1)
// 其他按钮消息映射...
END_MESSAGE_MAP()
4.3 数字输入处理
实现数字输入的核心逻辑,根据运算符编辑框是否为空,决定数字输入到哪个操作数编辑框:
cpp复制void CSimpleCalculatorDlg::input(char* a)
{
GetDlgItemText(IDC_EDIT2, str2); // 获取运算符编辑框内容
if (str2 == _T("")) { // 如果运算符为空,输入到第一个操作数
str1 += LPCTSTR(a);
number1 = _ttof(str1);
SetDlgItemText(IDC_EDIT1, str1);
}
else { // 否则输入到第二个操作数
str3 += LPCTSTR(a);
number2 = _ttof(str3);
SetDlgItemText(IDC_EDIT3, str3);
}
}
4.4 运算符处理
为加减乘除运算符按钮实现点击事件处理:
cpp复制void CSimpleCalculatorDlg::OnBnClickedBtnAdd()
{
str2 = _T("+");
SetDlgItemText(IDC_EDIT2, str2);
}
void CSimpleCalculatorDlg::OnBnClickedBtnSubtract()
{
str2 = _T("-");
SetDlgItemText(IDC_EDIT2, str2);
}
// 乘除运算符处理类似...
4.5 计算与结果显示
实现等号按钮的点击事件,执行实际计算并显示结果:
cpp复制void CSimpleCalculatorDlg::OnBnClickedBtnEqual()
{
GetDlgItemText(IDC_EDIT1, str1);
GetDlgItemText(IDC_EDIT2, str2);
GetDlgItemText(IDC_EDIT3, str3);
number1 = _ttof(str1);
number2 = _ttof(str3);
if (str2 != _T("")) {
if (str2 == _T("+")) {
result = number1 + number2;
}
else if (str2 == _T("-")) {
result = number1 - number2;
}
else if (str2 == _T("*")) {
result = number1 * number2;
}
else if (str2 == _T("/")) {
if (number2 != 0) {
result = number1 / number2;
} else {
MessageBox(_T("除数不能为零!"));
return;
}
}
else {
MessageBox(_T("运算符输入有误!"));
return;
}
str.Format(_T("%g"), result);
SetDlgItemText(IDC_EDIT4, str);
}
}
4.6 清零功能实现
清零按钮需要重置所有编辑框和内部变量:
cpp复制void CSimpleCalculatorDlg::OnBnClickedBtnClear()
{
str1 = _T("");
str2 = _T("");
str3 = _T("");
str = _T("");
number1 = 0;
number2 = 0;
result = 0;
SetDlgItemText(IDC_EDIT1, str1);
SetDlgItemText(IDC_EDIT2, str2);
SetDlgItemText(IDC_EDIT3, str3);
SetDlgItemText(IDC_EDIT4, str);
}
5. 调试与优化
5.1 常见问题排查
在开发过程中,我遇到了几个典型问题:
-
按钮点击无响应:
- 检查消息映射是否正确定义
- 确认按钮ID与处理函数中的ID一致
- 确保没有重复的按钮ID
-
计算结果不正确:
- 检查字符串到浮点数的转换是否正确
- 验证运算符判断逻辑
- 添加调试输出,检查中间变量值
-
界面布局混乱:
- 使用对话框编辑器中的对齐工具
- 设置控件的Tab顺序
- 考虑使用网格布局
5.2 代码优化建议
- 使用枚举代替魔术字符串:
cpp复制enum Operator { ADD, SUBTRACT, MULTIPLY, DIVIDE };
- 添加输入验证:
cpp复制// 检查输入是否为有效数字
bool IsValidNumber(const CString& str)
{
// 实现验证逻辑
}
- 支持连续计算:
cpp复制// 在等号计算后,将结果作为第一个操作数
str1 = str;
number1 = result;
str2 = _T("");
str3 = _T("");
SetDlgItemText(IDC_EDIT1, str1);
SetDlgItemText(IDC_EDIT2, str2);
SetDlgItemText(IDC_EDIT3, str3);
6. 实验心得与扩展思考
通过这次MFC计算器开发实验,我获得了以下宝贵经验:
-
MFC框架理解:
- 深入理解了MFC的消息映射机制
- 掌握了对话框应用程序的基本结构
- 熟悉了常用控件的使用方法
-
开发实践技巧:
- 学会了如何组织代码结构
- 掌握了基本的调试技巧
- 理解了用户界面与业务逻辑的分离
-
问题解决能力:
- 培养了独立排查问题的能力
- 学会了如何查阅MSDN文档
- 掌握了向同学和老师请教的有效方式
对于想要进一步扩展此项目的同学,我建议考虑以下方向:
-
功能扩展:
- 添加更多数学函数(平方根、幂运算等)
- 支持括号和运算优先级
- 实现历史记录功能
-
界面改进:
- 使用更现代的UI风格
- 添加皮肤切换功能
- 支持窗口大小调整
-
代码优化:
- 采用MVC模式重构代码
- 添加单元测试
- 实现国际化支持
这个项目虽然基础,但涵盖了MFC开发的许多核心概念。通过不断迭代和完善,可以逐步提升自己的Windows桌面开发能力。