非可视化编程的windows窗口 C++ 代码设计:附例程并多多知识点

我用Dev-C++写了一个的非可视化编程条件下的windows窗口计算器例程,在此分享给广大初学者。其运行效果如下图:

所有框架和单目运算已经做好,+-*/暂未完成,代码还有改进空间...... 

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <array>
#include <cmath>
#include <windows.h>
 
using namespace std;

#define myClassName "WindowCalculator"
#define NUM (sizeof Buttons / sizeof Buttons[0])
#define WarningBeep Beep(400,100)
#define OutErrorText(s) SetWindowText(tHwnd[1], TEXT(s))
#define OutResultText SetWindowText(tHwnd[2], tmp.c_str())
#define ClearText SetWindowText(tHwnd[1],"");SetWindowText(tHwnd[2],"0")
#define AboutMe ShellAbout(hwnd, "我的计算器",\
	"我的Windows窗口计算器 by Hann Yang, 2021.2.21", NULL)

struct button {
	int x;	int y;  //坐标
	int w;	int h;  //宽高
	const char *szText;  //Caption
}
Buttons[] = {
	30, 370,0,0,TEXT("0"),
	30, 310,0,0,TEXT("1"),
	90, 310,0,0,TEXT("2"),
	150,310,0,0,TEXT("3"),
	30, 250,0,0,TEXT("4"),
	90, 250,0,0,TEXT("5"),
	150,250,0,0,TEXT("6"),
	30, 190,0,0,TEXT("7"),
	90, 190,0,0,TEXT("8"),
	150,190,0,0,TEXT("9"),
	150,370,0,0,TEXT(" ."),
	210,370,0,0,TEXT("+"),
	210,310,0,0,TEXT("-"),
	210,250,0,0,TEXT("×"),
	210,190,0,0,TEXT("÷"),
	210,130,0,0,TEXT("±"),
	270,310,0,0,TEXT("="),
	270,250,0,0,TEXT("1/x"),
	270,190,0,0,TEXT("√x"),
	270,130,0,0,TEXT("y2"),
	150,130,0,0,TEXT("%"),
	90, 130,0,0,TEXT("C"),
	30, 130,0,0,TEXT("←")
};
		
HFONT hFont[3];
HWND tHwnd[3], bHwnd[NUM];

string GetStaticText(short i=2)
{
	char buf[64] = {0};
	GetWindowText(tHwnd[i], buf, 64);
	return string(buf);
}

void myCreateFont()
{
	hFont[0] = (HFONT)GetStockObject(SYSTEM_FIXED_FONT);
	/* windows系统基本字体:ANSI_FIXED_FONT DEFAULT_GUI_FONT ANSI_VAR_FONT
		DEVICE_DEFAULT_FONT SYSTEM_FIXED_FONT SYSTEM_FONT OEM_FIXED_FONT */
	hFont[1] = CreateFont(
                -15, -8, /* 字符的逻辑单位高宽值(也被称为em高度)
                		>0:字体映射器转换这个值以设备单位,并和已有字体的单元高度相匹配。
						=0:字体映射器转换在选择匹配时用一个缺省的高度值。
						<0:字体映射器转换这个值到设备单位,并将它的绝对值和已有字体的字符高度相匹配。 */ 
				0,  /*指定移位向量和设备X轴之间的一个角度,以十分之一度为单位*/ 
				0, /*指定每个字符的基线和设备X轴之间的角度*/
				400, /*字体权值:400表示标准体,700表示黑(粗)体*/
                FALSE, FALSE, FALSE, /*字体样式参数对应:斜体、下划线、删除线*/
                DEFAULT_CHARSET,  /*默认字符集*/
                OUT_DEFAULT_PRECIS, /*默认裁剪状态*/
				CLIP_DEFAULT_PRECIS, /*默认输出精度*/
                DEFAULT_QUALITY,  /*默认输出质量*/
                DEFAULT_PITCH|FF_DONTCARE,  /*默认字体间距|不关心字体族*/
                TEXT("微软雅黑") ); /*字体名称*/
	hFont[2] = CreateFont(
                30, 10, 0, 0, 700, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
				OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE,
                TEXT("仿宋") );
}

void myCreateWindow(HWND hwnd, LPARAM lParam)
{
	tHwnd[0] = CreateWindow( TEXT("button"),NULL,
					WS_CHILD | WS_VISIBLE | BS_GROUPBOX, /*分组框*/
                    30, 25, 290, 80,
					hwnd,(HMENU)31,
					((LPCREATESTRUCT) lParam)->hInstance,NULL);
	if (!tHwnd[0]) MessageBox(NULL,"创建分组框失败","Message",MB_OK|MB_ICONERROR);
	ShowWindow(tHwnd[0], SW_SHOW);
	UpdateWindow(tHwnd[0]);
            
	tHwnd[1] = CreateWindow( TEXT("static"),NULL,
					WS_CHILD | WS_VISIBLE ,
                    36, 40, 278, 22,
					hwnd,(HMENU)32,
					((LPCREATESTRUCT) lParam)->hInstance,NULL);
	if (!tHwnd[1]) MessageBox(NULL,"创建文本框失败","Message",MB_OK|MB_ICONERROR);
	ShowWindow(tHwnd[1],SW_SHOW);
	SendMessage(tHwnd[1], WM_SETFONT, (WPARAM)hFont[1], lParam);  //设置控件字体
	SetWindowLong(tHwnd[1], GWL_STYLE, GetWindowLong(tHwnd[1], GWL_STYLE)|ES_RIGHT|SS_CENTERIMAGE); //设置右对齐、垂直居中 
	UpdateWindow(tHwnd[1]);
            
	tHwnd[2] = CreateWindow( TEXT("static"),"0",
					WS_CHILD | WS_VISIBLE ,
                    36, 62, 278, 35,
					hwnd,(HMENU)33,
					((LPCREATESTRUCT) lParam)->hInstance,NULL);
	if (!tHwnd[2]) MessageBox(NULL,"创建文本框失败","Message",MB_OK|MB_ICONERROR);
	ShowWindow(tHwnd[2],SW_SHOW);
	SetWindowLong(tHwnd[2], GWL_STYLE, GetWindowLong(tHwnd[2], GWL_STYLE) | ES_RIGHT);
	SendMessage(tHwnd[2], WM_SETFONT, (WPARAM)hFont[2], lParam);
	UpdateWindow(tHwnd[2]);
            
	for(int i = 0; i < NUM; i++) {
		Buttons[i].w = 50;
		Buttons[i].h = 50;
		Buttons[0].w = 110;	 //0双倍宽 
		Buttons[16].h = 110; //=双倍高 
 
		bHwnd[i] = CreateWindow( TEXT("button"),
						Buttons[i].szText,
						WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_FLAT,
						Buttons[i].x, Buttons[i].y,
						Buttons[i].w, Buttons[i].h,
						hwnd,
						(HMENU)(WPARAM)i, //(WPARAM)强制转换用于消除警告 
						((LPCREATESTRUCT) lParam)->hInstance, NULL);
		if (!bHwnd[i]) MessageBox(NULL,"创建命令按钮失败","Message",MB_OK|MB_ICONERROR);
		ShowWindow(bHwnd[i],SW_SHOW);
		SendMessage(bHwnd[i], WM_SETFONT, (WPARAM)hFont[0], lParam);  //设置控件字体
		UpdateWindow(bHwnd[i]);	
	}
}

template<typename T>string num2str(T d)
{
	string s;
	stringstream ss;
	ss<<setprecision(15)<<d;
	s=ss.str();
	ss.clear();
	return s;
}
 
template<typename T>T str2num(string s)
{
	T d;
	stringstream ss;
	ss<<s;
	ss>>setprecision(15)>>d;
	ss.clear();
	return d;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	string tmp;
	array <string,4> strSign={"+","-","*","/"};
	static bool num_input_state = true;
	static bool sgn_input_state = false;
	unsigned int lWord = LOWORD(wParam);

	switch(Message) {
		case WM_CREATE:  // WM_CREATE事件中创建所有子窗口控件 
			myCreateFont();
			myCreateWindow(hwnd, lParam);
			break;

		case WM_COMMAND:  // WM_COMMAND响应的wParam值对应控件的(HMENU)(WPARAM)i参数
			tmp = GetStaticText();
			if(lWord>=0 && lWord<=10){ //0~9、.
				if (lWord==10 && tmp.find(".")!=tmp.npos){
					OutErrorText("无效输入:已有小数点");
					WarningBeep;
					break;
				}
				if (sgn_input_state) tmp="";
				sgn_input_state = false;
				tmp = (tmp=="0"&&lWord!=10?"":tmp) + (lWord==10?".":num2str<int>(lWord));
				if (num_input_state) OutResultText; 
			}
			else if(lWord>=11 && lWord<=14){ //+、-、*、/ 
				num_input_state = true;
				sgn_input_state = true;
				if (GetStaticText(1).back()=='+'){
					//MessageBox(hwnd, "+++", NULL, 0);
				}
				tmp = tmp + strSign.at(lWord-11);
				OutErrorText(tmp.c_str());
			}
			else{
				num_input_state = false;
				switch (lWord){
				case 15: //±
					OutErrorText(("-("+tmp+")").c_str());
					if (tmp.substr(0, 1) == "-")
						tmp.erase(tmp.begin(),tmp.begin()+1);
					else
						if (tmp!="0") tmp = "-" + tmp;
					OutResultText;
					break;
				case 16: // =
					
					num_input_state = true;
					break;
				case 17: //1/x
					if (tmp=="0") {
						OutErrorText("除0错:#DIV/0!");
						WarningBeep;
						break;
					}
					OutErrorText(("1/"+tmp).c_str());
					tmp = num2str<long double>(1/str2num<long double>(tmp));
					OutResultText;
					break;
				case 18: //sqrt(x)
					if (str2num<long double>(tmp)<0) {
						OutErrorText("无效输入:负数开平方");
						WarningBeep;
						break;
					}
					OutErrorText(("sqrt("+tmp+")").c_str());
					tmp = num2str<long double>(sqrt(str2num<long double>(tmp)));
					OutResultText;
					break;
				case 19: //y^2
					OutErrorText(("pow("+tmp+",2)").c_str());
					tmp = num2str<long double>(pow(str2num<long double>(tmp),2.0));
					OutResultText;
					if (GetStaticText()=="inf"){
						OutErrorText("无效输入:平方值超出允许范围");
						WarningBeep;
						tmp="0";
						OutResultText;
					}
					break;
				case 20: //%
					if (tmp.find("e")==tmp.npos)
						OutErrorText((tmp+"%").c_str());
					else
						OutErrorText(("0.01*("+tmp+")").c_str());
					tmp = num2str<long double>(str2num<long double>(tmp) * 0.01);
					OutResultText;
					break;
				case 21: //C clear
					ClearText;
					num_input_state = true;
					break;
				case 22: //BackSpace
					tmp = tmp.substr(0, tmp.size()-1);
					if (tmp.empty()) tmp = "0";
					OutResultText;
					num_input_state = true;
					break;
				default:
					MessageBox(hwnd, num2str<int>(lWord).c_str(), NULL, 0);
				}
			}
			break; //end case WM_COMMAND
		
		case WM_KEYDOWN:
			//返回的wParam为按键的虚拟键码: 110(orNumPAD190)=='.' 48~57(orNumPAD96~105)=='0'~'9'
			tmp = num2str<int>(LOWORD(wParam)).c_str();
			OutResultText;
			break;
				
		case WM_SYSCHAR:
			//返回的wParam为按键的虚拟键码: 110(orNumPAD190)=='.' 48~57(orNumPAD96~105)=='0'~'9'
			tmp = num2str<int>(LOWORD(wParam)).c_str();
			OutResultText;
			break;
			
		case WM_CLOSE:
			if (IDYES==MessageBox(hwnd, "是否真的要退出?", "确认", MB_ICONQUESTION | MB_YESNO))
				DestroyWindow(hwnd);  //销毁窗口
			return 0;
 
		case WM_HELP: //响应 F1功能键 
			AboutMe;
			break;
 
		case WM_DESTROY:
			AboutMe;
			PostQuitMessage(0);
			return 0;
		
		default:
			return DefWindowProc(hwnd, Message, wParam, lParam);
	}
	return 0;
}

ATOM myRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wc;
	memset(&wc, 0, sizeof(wc));
	wc.cbSize		 = sizeof(WNDCLASSEX);
	wc.lpfnWndProc	 = WndProc;
	wc.hInstance	 = hInstance;
	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);
 
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName  = NULL;	//无菜单;MAKEINTRESOURCE(IDC_MYMENU);
	wc.lpszClassName = myClassName;
	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION);
	wc.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION);

	return RegisterClassEx(&wc);
}

BOOL myInitInstance(HINSTANCE hInstance, int nCmdShow)
{
	HWND hwnd;
	RECT rect;
	int dtWidth, dtHeight;
	hwnd = GetDesktopWindow(); //取桌面句柄 
	GetWindowRect(hwnd,&rect); //取桌面范围 
	dtWidth = rect.right-rect.left; //桌面宽度 
	dtHeight = rect.bottom-rect.top; //桌面高度 
 
	hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
		myClassName,
		TEXT("我的计算器"),
		WS_VISIBLE|WS_OVERLAPPED|WS_SYSMENU|WS_MINIMIZEBOX,
		(dtWidth-360)/2,   /*窗体居中*/ 
		(dtHeight-480)/2,
		360, 480,
		NULL,NULL,hInstance,NULL);
	if (!hwnd) return false;
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	return true;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG msg;
	HWND hwnd;

 	if(!myRegisterClass(hInstance)) {
		MessageBox(NULL, "Window Registration Failed!","Error!", MB_ICONEXCLAMATION|MB_OK);
		return 0;
	}
 
	if(!myInitInstance(hInstance, nCmdShow)) {
		MessageBox(NULL, "Window Creation Failed!","Error!", MB_ICONEXCLAMATION|MB_OK);
		return 0;
	}
 
	while(GetMessage(&msg, NULL, 0, 0) > 0) { 
		if(!IsDialogMessage(hwnd, &msg)){  
			TranslateMessage(&msg); 
			DispatchMessage(&msg);
		}
		//IsDialogMessage使得TAB和ARROW键可切换控件焦点,前提是控件有设WS_TABSTOP参数
	}

	return msg.wParam;
}

感兴趣的话可以阅读一下本文的前篇:《C++ 用DEV-C++建一个Windows窗口程序带文本框和命令按钮》。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hann Yang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值