vsMFC计算器制作.doc
制作计算器本文采用动态创建控件的方法,使用 vs2019 MFC,仿照windows自带的计算器进行制作。通过这个程序可以使你了解:1 如何使用文本框、按钮2 如何通过控件的ID获取控件指针3 得到控件指针后可以对控件做些什么4 字符串如何处理(拼接、查找、转换等)5 如何给控件绑定响应事件6 如何重载原有的函数7 其他自己体会界面分析:不要上面的菜单栏,也不要第一排的按钮,这些按钮都不要,只要一个文本框和下面右图中的按钮,布局方式也按照这个图布局。仔细数一数,共有23个按钮和一个编辑框。功能分析:用一下windows的计算器,可以发现:1. 初始状态为02. 如果按下数字键,这个0就会变成相应的数字,如果按下小数点,就会变为“0.”3. 除数不能为04. 连续按下多个运算符,以最后一个计算符为准5. 每次按下运算符都会把之前的结果先经行计算,例如:你已经输入了 1+ 1 此时再按下 “+”,就会先把1+1进行计算,保存结果2.6. 这个计算器分两行显示,上面一行显示算式,下面显示结果.7. 当你输入一个数字和一个运算符就按下“=”时,第二个数默认和第一个相同,例如:“3*”按下“=”,显示“9”8. 开平方不能是负数9. 其它异常暂不处理新建项目这里我给项目命名为:Calculator应用程序的类型选择 基于对话框,点击完成完成之后,先来观察一下项目中都有哪些东西:资源视图(下面右图)是我们的界面,解决方案中的是我们的源文件(下面左图)。我们的代码主要写在和中,.h文件中主要定义一些东西,.cpp文件中写具体的东西。准备知识初始界面打开界面,可以看到,上面已经给我们放上去了两个按钮确定和取消。由于我们建立的是对话框应用程序,这两个按钮的功能就是接受Enter键和ESC键的响应。先不要删除它,不然会造成不必要的麻烦:例如如果你删除了这两个按钮,当你在界面上按下回车时,程序找不到响应的控件,就会导致异常。添加编辑框在工具箱中找到编辑框控件,把它拖放到界面合适的位置,并在属性栏中把它的ID改为TEXTC,便于记忆,这个编辑框的作用就是显示数字。有的资料会给这个编辑框关联一个值,用于处理编辑框中内容,在这里,我们就不给他关联值,要想给编辑框显示一个值或者是获取文本框中的值,通过代码去实现。包括后面按钮的处理,我们都尽量通过代码去实现它的功能。至此,一行代码还没有写,先运行一下看看(如下图),如果你在文本框中按下回车键或者ESC键,就会发现程序会直接退出。这是因为“确定”按钮响应回车事件,“取消”按钮响应ESC键,如果不想退出,可以重载这两个按钮的响应事件,也可以屏蔽键盘的输入,这里暂不处理。(换句话说,如果不建立对话框应用程序,不就可以了吗?没办法暂时先学一学这个吧),如果觉着两个按钮很碍眼,可以把他们隐藏掉:把这两个按钮的Visible属性改为false即可。设置编辑框可以换行,有三个属性要更改;Auto HScroll 设置为 FalseMultiLine 设置为 TrueWant Return 设置为 True代码写法:CEdit* pEdit = (CEdit*)GetDlgItem(TEXTC); /断言,判断是不是pEdit(类似if语句) ASSERT(pEdit && pEdit->GetSafeHwnd(); /pEdit->SetSel(-1); /追加模式 pEdit->ReplaceSel(_T("第一行rn第二行"); 或者SetDlgItemText(TEXTC,_T("ArnB"); rn就是换行符GetDlgItem 这个方法很有用,要注意!读/写编辑框中内容编辑框已经添加上去,那么怎样使用这个编辑框呢?主要就是怎样获取它里面的内容或者让它显示指定的内容。主要使用到两个函数:读取控件中的内容:写入控件内容:nID:控件的ID,也可以使控件的名字rString:保存到的字符串(即:将控件上的文本获取到rString中)lpszString:要显示出来的字符串。需要注意的是这个参数是LPCTSTR类型,不是CString类型,在使用的过程中要进行转换:(LPCTSTR)str怎样在编辑框后面追加内容,不删除之前的内容呢?后面会讲到。在界面上添加按钮定义你可以一个一个按钮拖放上去,但这里我使用的是通过代码进行创建。根据开始时候的分析,共需要23个按钮,于是我们需要定义一个长度为23的按钮数组,为了便于更改,这里定义一个常量NUM,表示23. 如下:public: static const int NUM = 23;public: CButton *p_ButtonNUM ;除此之外,为了便于写代码,再定义一个函数,用了创建按钮,如下:public: CButton* NewMyButton(LPCTSTR m_Caption,int nID,CRect rect);LPCTSTR m_Caption:m_Caption是按钮上显示的文字,LPCTSTR是参数类型int nID:是按钮的ID号CRect rect:rect是按钮的左上角坐标和大小,原型顺便再定义几个变量,后面写代码要用到:CString equation ;/算式CString inputNum ;/输入的数字CString op;/运算符double result;/计算结果double number;/输入的数字前面说过,定义是放在“.h”文件中的,打开文件,写在里面就可以了。写完之后先运行一下试试看,如果没有报错,就说明你写对了,如果报错,就这几行定义你也能写错?!接下来进入主题:初始化在窗体初始化的时候,就应该把按钮添加上去。打开文件,在里面找到BOOL CCalculatorDlg:OnInitDialog() 这个函数,这就是初始化的函数,要不创建按钮的工作就写在这里。首先你要根据按钮排列的特点确定算法,这里就不详细解释了:CString OP = _T(""),_T("CE"),_T("C"),_T("±"),_T(""),_T("7"),_T("8"),_T("9"),_T("/"),_T("%"),_T("4"),_T("5"),_T("6"),_T("*"),_T("1/x"),_T("1"),_T("2"),_T("3"),_T("-"),_T("="),_T("0"),_T("."),_T("+");/上面是按钮上的内容,从第一行按顺序记录 int tm=0; /记录是第几个按钮 int x=10;/第一个按钮的起始x坐标IDC_D_BTN 这个常量是自己定义的,表示按钮的起始ID,为了不和其他控件的ID冲突,就把它定义为10000.Resource.h文件中进行定义:#define IDC_D_BTN 10000 int y=60;/第一个按钮的起始y坐标 int w = 40;/ 按钮的宽度 int h = 33;/ 按钮的高度 int d=3;/ 按钮之间的间距 for(int i=0;i<NUM;i+)int r = tm/5;/对应的行int c=tm%5;/对应的列if(OPi = _T("=")/“=”按钮比较大,占了两行p_Buttoni =NewMyButton(LPCTSTR)OPi ,IDC_D_BTN+i,CRect(x +w*c,y +h*r,x +w*(c+1)-d,y+h*(r+2)-d);else if(OPi = _T("0")/“=”按钮比较大,占了两列p_Buttoni =NewMyButton(LPCTSTR)OPi ,IDC_D_BTN+i,CRect(x +w*c,y +h*r,x +w*(c+2)-d,y+h*(r+1)-d);tm=tm+1;else/其他的按钮都是正常的大小p_Buttoni =NewMyButton(LPCTSTR)OPi ,IDC_D_BTN+i,CRect(x +w*c,y +h*r,x +w*(c+1)-d,y+h*(r+1)-d); tm=tm+1;/顺便把其他变量也给初始化了/SetDlgItemText(TEXTC,(LPCTSTR)"0"); CEdit* pEdit = (CEdit*)GetDlgItem(TEXTC); /断言,判断是不是pEdit(类似if语句)ASSERT(pEdit && pEdit->GetSafeHwnd();pEdit->ReplaceSel(_T("rn0"); equation="" result = 0; op = ""inputNum="0" number = 0;上面使用了之前定义的创建按钮的函数:NewMyButton,下面添加这个函数在这个cpp文件中,找个位置,写上如下函数:CButton* CCalculatorDlg:NewMyButton(LPCTSTR m_Caption,int nID,CRect rect)CButton *p_B = new CButton();p_B->Create( m_Caption,WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, rect, this, nID ); return p_B;注:Create方法有很多作用,可以创建各种控件完成以上工作后,尝试运行一下,如果没有错误,就会出现如下界面:稍微把窗体的大小调整合适 按钮已经创建出来了,但是别高兴的太早,当你关闭这个界面的时候,会发现提示很多的错误,大意是内存泄露,原因是程序中实例化了按钮,关闭程序的时候却没有释放它所占有的空间,所以在关闭窗体的事件中,要对new出来的对象进行释放。下面就说一说怎样写这个关闭事件。重写函数的方法:点击选择Calculate窗体,在属性栏中找到,点击红色方框中的图标,在下面会出现很多的方法,点击对应函数后面的下拉框进行选择即可,如下图窗体关闭函数: 点击,即可在CalculatorDlg.cpp文件中创建关闭函数。在CDialog:OnClose();上面,写上释放空间的代码:for(int i=0;i<NUM;i+)/循环删除每一对象 if( p_Buttoni !=NULL)/如果该对象不是空,就进行删除delete p_Buttoni;至此,再运行一次程序,可以发现错误消失了。接下来,就要让这些按钮起作用,给它们绑定一个点击事件.绑定响应事件首先,在头文件中定义一个函数,作为按钮的单击事件:public: void OnButton(UINT nID);其中UINT nID 是触发该事件控件的ID号,根据这个ID就可以找到对应的按钮。在cpp文件中,写上这个函数的实现:void CCalculatorDlg:OnButton(UINT nID)CString tag ;/先获取文本框中的值,用于判断GetDlgItemText(TEXTC,tag); /获取按钮上的内容CString str;GetDlgItemText(nID,str); /接下来是对str进行判断,做出相应的处理,这个暂略 /后面会写这部分的代码,暂时让他弹出一个提示框,显示str AfxMessageBox(str);事件已经有了,接下来要把它绑定到每个按钮在cpp文件中找到下面的函数在那条注释的下一行写上下面代码:ON_COMMAND_RANGE(IDC_D_BTN,IDC_D_BTN+NUM,OnButton)解释:上面的代码段(BEGIN_MESSAGE_MAP这段)通过名字可以看得出是一个消息的MAP,类似字典。如果你拖一个按钮到界面上,并且给它添加一个点击事件,可以发现,在这段代码中会出现这个单击事件的函数。ON_COMMAND_RANGE的意思是消息响应的映射,专业点的说法是“宏定义”,在这里,表示的意思是:只要ID号在IDC_D_BTN和IDC_D_BTN+NUM之间的控件,都接收OnButton这个函数。在创建按钮的时候,我们为每个按钮分配了一个ID,ID值就是在IDC_D_BTN和IDC_D_BTN+NUM之间的,所以那些按钮可以响应这个事件,即:点击按钮时,会调用OnButton函数。至此,每个按钮的单击事件已经完成了,可以运行一下试试看:功能实现至此,基本的功能已经实现,下面就是纯代码了,没有什么复杂的操作,就是基本的逻辑处理。找到void CCalculatorDlg:OnButton(UINT nID)函数,在这里面写具体的功能。算法就不详细解释了,实现的方法也因人而异,可以自己去琢磨。下面给出基本的思路和代码的写法:注:代码中有些变量在上面没有提到,主要是这些变量仅仅与计算有关,与界面的设计无关,可以自行查看源代码。/按钮的点击事件void CCalculatorDlg:OnButton(UINT nID)/根据编辑框的名字(ID),获取编辑框CEdit* pEdit = (CEdit*)GetDlgItem(TEXTC); /断言,判断是不是pEdit(类似if语句)ASSERT(pEdit && pEdit->GetSafeHwnd();CString txt ;/先获取文本框中的值,用于判断/CString ts0 ;/ 编辑框第一行的值/CString ts ;/ 编辑框第二行的值GetDlgItemText(TEXTC,txt);/pEdit->getint pos= txt.Find(_T("rn"); /获取按钮上的内容CString bs;GetDlgItemText(nID,bs); CString sNum = _T("0123456789.");/数字字符串,包括小数点bool isEdit = false;/用了标记是否可以输入if(sNum.Find(bs)>=0)/如果按下的是数字if(bs="0")/如果按下的是,要判断一下if(inputNum="0")/如果编辑框中已经是,就不能再输入isEdit=false;elseisEdit=true;else if(bs=".")/如果按下的是小数点,要判断一下if(inputNum.Find('.')>0)/如果编辑框已经有小数点了,就再输入isEdit=false;else/如果编辑框没有小数点,就输入小数点isEdit=true;/ AfxMessageBox(bs);else/其他数字不用判断 if(inputNum="0")/如果编辑框中是,就把这个去掉SetDlgItemText(TEXTC,_T("rn"); inputNum = ""/AfxMessageBox(inputNum);isEdit=true;if(isEdit=true)/如果可以输入,就把bs追加到编辑框inputNum +=bs;SetDlgItemText(TEXTC,equation+_T("rn")+inputNum); /pEdit->ReplaceSel(bs); /上面一段if是控制输入else/操作符,下面是进行计算处理if(inputNum="")/什么也没有输入else/编辑框中有数字if(bs="+" | bs="-"| bs="*"| bs="/"| bs="=")/number = _wtof(ts); /当前的数字给numberif(tag)/第一次输入数字 result =_wtof(inputNum); tag=false;if(inputNum="")CString cs ;cs.Format(_T("%G"),result);SetDlgItemText(TEXTC,equation+bs+_T("rn")+cs); elseif(op="+") result +=_wtof(inputNum);else if(op="-") result -=_wtof(inputNum);else if(op="*") result *=_wtof(inputNum);else if(op="/") result /=_wtof(inputNum);/其他运算符在此添加equation += inputNum;equation += bs;CString cs ;cs.Format(_T("%G"),result);SetDlgItemText(TEXTC,equation+_T("rn")+cs); if(bs="=")equation = ""CString cs ;cs.Format(_T("%G"),result);SetDlgItemText(TEXTC,equation+_T("rn")+cs); op = bs;/记录当前按下的操作符 inputNum = ""if(bs="CE")/复原初始状态pEdit->ReplaceSel(_T("rn0"); equation=""inputNum="0"result = 0;number = 0;op = ""tag = true;/其他操作符处理在此添加 / -编辑框中有数字/ - 操作符/end 第 18 页