c++学习 | Windows 窗口界面

  • Post author:
  • Post category:其他


学习资料

VC驿站


课程资料:

课程资料地址


视频讲解:

视频讲解地址




一、界面开发三步走

①纯API进行窗口的[手动]创建+[手动]创建控件+[手动]响应消息;

②纯API进行对话框窗口的[半手动]创建+[自动]创建控件+[手动]响应消息;

③MFC窗口的[自动]创建+[自动]创建控件+[自动]响应消息;

备注。为保持程序的灵活性,以及让大家学到更多的东西,针对第③点我们只是界面部分用MFC,其余逻辑部分仍然用WinAPI进行开发。



二、纯API进行窗口的[手动]创建+[手动]创建控件+[手动]响应消息



1.Win32窗口程序的创建流程[手动]

第一步:注册窗口类,指定窗口类的名称与窗口回调函数

第二步:创建窗口,指定注册窗口类,窗口标题,窗口的大小

第三步:显示窗口

第四步:开始消息循环

第五步:窗口函数中处理窗口消息


代码如下:

#include <windows.h>
#include <tchar.h>

static TCHAR szWindowClass[] = _T("WindowClass"); //窗口类名
static TCHAR szTitle[] = _T("我的第一个程序"); //窗口标题

//第五步:窗口函数中处理窗口消息
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
//消息处理函数.参数:窗口句柄,消息,消息参数,消息参数
{
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("大家好!"); //窗口内容

	switch (uMsg)//处理所需的消息
	{
	    case WM_CLOSE://窗口关闭的时候添加确认提示框,取消则不关闭窗口
			{
			int iRet = MessageBox(hWnd, _T("要关闭窗口吗?"), _T("Tip"), MB_OKCANCEL);
			if (iRet == IDOK) return DefWindowProc(hWnd, uMsg, wParam, lParam);
			}
			break;
		case WM_SIZE://窗口被最大化的时候添加 MessageBox 提示框;
			{
				if (wParam == SIZE_MAXIMIZED)
				{MessageBox(hWnd, _T("窗口被最大化啦!"), _T("Tip"), MB_OK);}
			}
			break;
		case WM_CREATE://窗口创建的时候添加提示框;
	        MessageBox(hWnd, _T("窗口被创建啦!"), _T("Tip"), MB_OK);
	        break;
		case WM_PAINT://窗口绘制消息
			hdc = BeginPaint(hWnd, &ps);
			TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY://当用户关闭窗口,窗口销毁,程序需结束,发退出消息,以退出消息循环
			PostQuitMessage(0);
			break;
		default://其他消息交给由系统提供的缺省处理函数
			return DefWindowProc(hWnd, uMsg, wParam, lParam);
			break;
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
//应用程序主函数。参数:实例句柄、前一个实例的句柄、命令行参数、窗口显示方式
{
	//第一步:注册窗口类,指定窗口类的名称与窗口回调函数
	WNDCLASSEX wcex = { 0 };//RegisterClassEx中的结构指针
	wcex.cbSize = sizeof(WNDCLASSEX);//WNDCLASSEX 的大小
	wcex.lpfnWndProc = WindowProc; //关联消息处理函数
	wcex.lpszClassName = szWindowClass;//类名称

	if (!RegisterClassEx(&wcex))
	//RegisterClassEx注册窗口类,如果函数成功,返回这个窗口类型的标识号;如果函数失败,返回值为FALSE
	{
		MessageBox(NULL, _T("注册窗口失败"), _T("Tip"), NULL);
		return 1;
	}


	//第二步:创建窗口,指定注册窗口类,窗口标题,窗口的大小
	HWND hWnd = CreateWindow( //创建一个重叠窗口
		szWindowClass, //窗口类名
		szTitle, //窗口标题,最开头定义过
		WS_OVERLAPPEDWINDOW, //指定创建窗口的风格
		CW_USEDEFAULT, CW_USEDEFAULT,//窗口初始位置
		500, 300, //窗口初始宽、高
		NULL, //指向被创建窗口的父窗口或所有者窗口的句柄
		NULL, //菜单句柄,或依据窗口风格指明一个子窗口标识
		hInstance, //与窗口相关联的模块实例的句柄。
		NULL //指向一个值的指针,该值传递给窗口WM_CREATE消息。
		);

	if (!hWnd)//CreateWindow函数执行成功,返回新创建窗口的窗口句柄,函数执行失败返回NULL
	{
		MessageBox(NULL, _T("创建窗口失败!"), _T("Tip"), NULL);
		return 1;
	}

	 //第三步:显示窗口
	ShowWindow(hWnd, nCmdShow);//窗口句柄,窗口激活方式

	// 第四步:开始消息循环
	MSG msg = { 0 };
	while (GetMessage(&msg, NULL, 0, 0))//从消息队列中取消息 
	{
		TranslateMessage(&msg); // 将虚拟键消息转换为字符消息
		DispatchMessage(&msg);//派发消息
	}

	return (int)msg.wParam;
}



2.Win32窗口控件的创建与响应[手动]

(1)控件的创建时机

①在WinMain函数中创建,即调用完CreateWindow函数创建完父窗口之后,再调用CreateWindow函数来创建主窗口上面的子控件;

②在窗口创建后,显示之前,即调用CreateWindow函数创建主窗口返回之前,我们的窗口函数会收到WM_CREATE消息,此时,我们响应它的号召,艰苦奋斗的创建各个子控件。这种做法是常规正常的做法,我们以下也使用这种做法。

(推荐第二种)

#include <windows.h>
#include <tchar.h>
#include <commctrl.h>

static TCHAR szWindowClass[] = _T("WindowClass");
static TCHAR szTitle[] = _T("窗口标题");

//控件ID
UINT IDC_BUTTON_1 = 200;
UINT IDC_BUTTON_2 = 201;

//第五步:窗口函数中处理窗口消息
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("窗口中内容");

	switch (uMsg)
	{
		case WM_CREATE: //创建两个按钮
		{
			HWND hButtonWnd1 = CreateWindow(WC_BUTTON, _T("按钮一"), WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 100, 100, 100, 50, hWnd, (HMENU)IDC_BUTTON_1, NULL, NULL);
			HWND hButtonWnd2 = CreateWindow(WC_BUTTON, _T("按钮二"), WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 100, 200, 100, 50, hWnd, (HMENU)IDC_BUTTON_2, NULL, NULL);
			//1、ws_CHILD样式:控件是放在窗口上的,自然要作为窗口的子窗口;
			//2、ws_VISIBLE样式:既然要使用控件,自然要让别人看得见;
			//3、BS_PUSHBUTTON样式:作为下压式按钮存在。
			//4、控件基本都要指定一个整型的不重复的ID,这样以后操作起来可以直接使用控件的ID来找到并操作控件,不然还得记住每个控件的窗口句柄。

		}
			break;
			
		case WM_COMMAND:
		{
			UINT nCtrlID = LOWORD(wParam); //低字节表示ID号
			UINT nCode   = HIWORD(wParam); //高字节表示通知码BN_CLICKED

			if (nCode == BN_CLICKED)
				{
				if (nCtrlID == IDC_BUTTON_1)
					{
						MessageBox(hWnd, _T("按键1被按下了"), _T("Tip"), MB_OK);
					}
				else if (nCtrlID == IDC_BUTTON_2)
					{
						MessageBox(hWnd, _T("按键2被按下了"), _T("Tip"), MB_OK);
					}
				}
			}
			break;
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd, uMsg, wParam, lParam);
			break;
		}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	//第一步:注册窗口类,指定窗口类的名称与窗口回调函数
	WNDCLASSEX wcex = { 0 };
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.lpfnWndProc = WindowProc;
	wcex.lpszClassName = szWindowClass;

	if (!RegisterClassEx(&wcex))
	{
		MessageBox(NULL, _T("注册窗口失败"), _T("Tip"), NULL);
		return 1;
	}


	//第二步:创建窗口,指定注册窗口类,窗口标题,窗口的大小
	HWND hWnd = CreateWindow(
		szWindowClass,
		szTitle,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		500, 300,
		NULL,
		NULL,
		hInstance,
		NULL
		);

	if (!hWnd)
	{
		MessageBox(NULL, _T("创建窗口失败!"), _T("Tip"), NULL);
		return 1;
	}

	第一种方式:在这里创建子窗口或者控件 CreateWindow

	 //第三步:显示窗口
	ShowWindow(hWnd, nCmdShow);

	// 第四步:开始消息循环
	MSG msg = { 0 };
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);//消息分发
	}

	return (int)msg.wParam;
}

(2)小作业

  • 作业1:按钮(标题)文本动态变化,显示该按钮被点击的次数,每点击一次,次数加1s;
  • 作业2:动态创建编辑框控件;
  • 作业3:点击按钮时,弹出提示框,提示框的内容是编辑框中的字符串;
//控件ID
UINT IDC_BUTTON_1 = 200;
UINT IDC_BUTTON_2 = 201;
UINT IDC_EDIT_1   = 301;

//第五步:窗口函数中处理窗口消息
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("窗口中内容!");

	switch (uMsg)
	{
	case WM_CREATE:
	{
		HWND hButtonWnd1 = CreateWindow(WC_BUTTON, _T("我是按钮1"), WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 30, 40, 180, 30, hWnd, (HMENU)IDC_BUTTON_1, NULL, NULL);
		HWND hButtonWnd2 = CreateWindow(WC_BUTTON, _T("我是按钮2"), WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 260, 40, 180, 30, hWnd, (HMENU)IDC_BUTTON_2, NULL, NULL);

		HWND hEditWnd1 = CreateWindow(WC_EDIT, _T("编辑框中文字"), WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE | ES_AUTOHSCROLL,260, 100, 180, 30, hWnd, (HMENU)IDC_EDIT_1, NULL, NULL); //作业2
	}
		break;
	case WM_COMMAND:
	{
		UINT nCtrlID = LOWORD(wParam);
		UINT nCode = HIWORD(wParam); //BN_CLICKED

		if (nCode == BN_CLICKED)
		{
			if (nCtrlID == IDC_BUTTON_1)
			{
				static int nButton1_Times = 0;
				TCHAR szButton1Times[128] = { 0 };
				_stprintf(szButton1Times, _T("按钮1被点击了 %d 次!"), ++nButton1_Times);
				//MessageBox(hWnd, szButton1Times, _T("Tip"), MB_OK);
				SetWindowText(GetDlgItem(hWnd, nCtrlID), szButton1Times); //作业1

				TCHAR szEditText[256] = { 0 };
				GetDlgItemText(hWnd, IDC_EDIT_1, szEditText, 256);
				MessageBox(hWnd, szEditText, _T("Tip"), MB_OK); //作业3
			}
			else if (nCtrlID == IDC_BUTTON_2)
			{
				static int nButton2_Times = 0;
				TCHAR szButton2Times[128] = { 0 };
				_stprintf(szButton2Times, _T("按钮2被点击了 %d 次!"), ++nButton2_Times);
				//MessageBox(hWnd, szButton2Times, _T("Tip"), MB_OK);
				SetWindowText(GetDlgItem(hWnd, nCtrlID), szButton2Times);//作业1
			}
		}
	}
	break;

(3)各类控件

UINT IDC_STATIC = 200;
UINT IDC_BUTTON = 201;
UINT IDC_RADIO1 = 301;
UINT IDC_RADIO2 = 302;
UINT IDC_SCBL = 303;
UINT IDC_COMBOX = 304;
UINT IDC_LISTBOX = 305;


switch (uMsg)
	{
	case WM_CREATE:
	{
	//创建静态文本框
	HWND hStatic = CreateWindow(TEXT("STATIC"), TEXT("Test String"), WS_CHILD|WS_VISIBLE, 10, 10, 100, 50,  hWnd, (HMENU)IDC_STATIC, NULL, NULL);
	SetWindowText(hStatic, L"更改后的内容!");
	//创建button
	CreateWindow(TEXT("BUTTON"), TEXT("Click Me"), WS_CHILD | WS_VISIBLE, 10, 40, 80, 20, hWnd, (HMENU)IDC_BUTTON, NULL, NULL);
	//创建单选框
	CreateWindow(TEXT("BUTTON"), TEXT("radio button"), WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 134, 10, 40, 20, hWnd, (HMENU)IDC_RADIO1, NULL, NULL);
	//创建复选框
	CreateWindow(TEXT("BUTTON"), TEXT("radio button"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 134, 40, 40, 20, hWnd, (HMENU)IDC_RADIO2, NULL, NULL);
	//创建滚动条
	CreateWindow(TEXT("SCROLLBAR"), NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ, 10, 70, 200, 20, hWnd, (HMENU)IDC_SCBL, NULL, NULL);
	//创建组合框
	CreateWindow(TEXT("COMBOBOX"), NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST, 10, 100, 100, 90, hWnd, (HMENU)IDC_COMBOX, NULL, NULL);
	//创建列表框
	CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD, 10, 140, 80, 90, hWnd, (HMENU)IDC_LISTBOX, NULL, NULL);
	}
		break;
}

在这里插入图片描述



3.Win32窗口高级列表控件的使用

(1)列表控件的操作流程(主动)

#include <windows.h>
#include <tchar.h>
#include <commctrl.h>

static TCHAR szWindowClass[] = _T("WindowClass");
static TCHAR szTitle[] = _T("窗口标题");

//控件ID
UINT IDC_BUTTON_1 = 200;
UINT IDC_BUTTON_2 = 201;
UINT IDC_LIST = 301;


//第五步:窗口函数中处理窗口消息
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR greeting[] = _T("窗口中内容");

	switch (uMsg)
	{
		case WM_CREATE: //创建两个按钮
		{
			HWND hButtonWnd1 = CreateWindow(WC_BUTTON, _T("按钮一"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 100, 50, 100, 50, hWnd, (HMENU)IDC_BUTTON_1, NULL, NULL);
			HWND hButtonWnd2 = CreateWindow(WC_BUTTON, _T("按钮二"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 100, 150, 100, 50, hWnd, (HMENU)IDC_BUTTON_2, NULL, NULL);

			HWND hListCtrl = CreateWindow(WC_LISTVIEW, _T(""), WS_CHILD | WS_VISIBLE | WS_BORDER|LWS_REPORT|LVS_SHOWSELALWAYS, 150, 200, 200, 200, hWnd, (HMENU)IDC_LIST, NULL, NULL);
			                                                //子控件   |  可见       |边框    |报表样式  | 一直显示选中...
		


	}
			break;
			
		case WM_COMMAND:
		{
			UINT nCtrlID = LOWORD(wParam); //低字节表示ID号
			UINT nCode   = HIWORD(wParam); //高字节表示通知码BN_CLICKED

			if (nCode == BN_CLICKED)
				{
				if (nCtrlID == IDC_BUTTON_1)
					{
						MessageBox(hWnd, _T("按键1被按下了"), _T("Tip"), MB_OK);
					}
				else if (nCtrlID == IDC_BUTTON_2)
					{
						MessageBox(hWnd, _T("按键2被按下了"), _T("Tip"), MB_OK);
					}
				}
			}
			break;
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd, uMsg, wParam, lParam);
			break;
		}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	//第一步:注册窗口类,指定窗口类的名称与窗口回调函数
	WNDCLASSEX wcex = { 0 };
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.lpfnWndProc = WindowProc;
	wcex.lpszClassName = szWindowClass;

	if (!RegisterClassEx(&wcex))
	{
		MessageBox(NULL, _T("注册窗口失败"), _T("Tip"), NULL);
		return 1;
	}


	//第二步:创建窗口,指定注册窗口类,窗口标题,窗口的大小
	HWND hWnd = CreateWindow(
		szWindowClass,
		szTitle,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		500, 300,
		NULL,
		NULL,
		hInstance,
		NULL
		);

	if (!hWnd)
	{
		MessageBox(NULL, _T("创建窗口失败!"), _T("Tip"), NULL);
		return 1;
	}

	第一种方式:在这里创建子窗口或者控件 CreateWindow

	 //第三步:显示窗口
	ShowWindow(hWnd, nCmdShow);

	// 第四步:开始消息循环
	MSG msg = { 0 };
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);//消息分发
	}

	return (int)msg.wParam;
}



三、MFC窗口的[自动]创建+[自动]创建控件+[自动]响应消息

工程自带的两个类

新生成的对话框工程主要有两个类:

主线程类



主对话框类



主线程类的初始化函数中创建主对话框,主对话框类负责界面显示。




总结

以上就是Windows 窗口界面的简单实现。



版权声明:本文为florence_jz原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。