欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Windows API入门:7:完善MessageBox

程序员文章站 2022-07-05 14:01:15
...

原作者BeYoNDCoDE,主页:http://blog.csdn.net/beyondcode
原文:https://blog.csdn.net/beyondcode/article/details/4043741

SDK编程群号:81543028 欢迎加入

各位不好意思,前几天有些事很忙,昨天才稍稍有时间空闲下来,所以我将我的API入门系列文章继续下去,上一篇《自己实现MessageBox》中我们基本已经实现了一个对话框了,可以在*显示自己的文字,并且显示一个确定按钮,可是,上一篇完的时候我留下了一个问题,那就是那个确定按钮并不会根据窗口的大小的改变而改变。那么我们怎么来解决这个问题呢?

我给出了提示可以通过处理WM_SIZE来完成这个目的。那么今天我们就来完成这一遗留的问题。所要使用到的新的API函数也不多,就两个,要处理的消息也就两个WM_SIZEWM_COMMAND这么两个,至于其他的API函数,都是我们以前接触过的,如果你忘记了,可以自己复习一下使用方法。

首先,我们先看WM_SIZE消息处理函数是怎么写的
(首先在前面声明两个变量)

	int buttonx, buttony;
	case WM_SIZE:
		GetClientRect(hwnd, &rctClient);//重新获取窗口大小
		buttonx = rctClient.right / 2 - buttonWidth / 2;
		buttony = rctClient.bottom / 2 - buttonHeight / 2;
		hButton = GetDlgItem(hwnd, 2);
		MoveWindow(hButton, buttonx, buttony, buttonWidth, 
			buttonHeight, TRUE);
	break;

由于这篇文章内容比较少,那么我就可以详细的介绍一下WM_SIZE这个消息处理函数中实现按钮始终保持居中的代码。WM_SIZE这个消息是当一个窗口的size也就是大小被改变后而被发送到该窗口的消息处理函数的。我们在这里通过截获WM_SIZE就可以在每次窗口大小被改变的时候进行一些处理,我们这里的处理就是将该窗口上的一个子窗口,也就是那个确定按钮移动到*。

首先,我们定义了两个整形变量buttonx,和buttony用来存放后面通过计算得到的确定按钮的左上角的坐标位置。

然后定义了一个RECT结构体用来保存后面通过GetClientRect API函数获取的窗口的长宽,其中rctClient中right就保存了窗口的长,bottom就保存了窗口的高。我们为了让按钮保持在主窗口的*,那么我们就需要让按钮的左上角的x坐标位置在主窗口的长的一半再减去按钮的长的一半的位置。高也是一样的原理。所以buttonx = rctClient.right/2 - buttonWidth/2; buttony = rctClient.bottom/2 - buttonHeight/2; 这两句就是根据当前主窗口的长和高计算按钮应该在的位置。

计算完成后,我们就只需要移动按钮就可以了。可是移动按钮之前,我们需要获得按钮的句柄,这个句柄怎么获得呢,有很多中方法,这里我就用GeDlgItem这个API函数来获取,它需要两个参数,第一个参数是一个主窗口的句柄,这里我们就传递按钮的主窗口的句柄hwnd,第二个参数是按钮的一个标识符,因为我们在前一篇文章中用CreateWindowEx创建子窗口的时候给按钮指定的标识符是2,所以这里我们就传递2,那么这样GetDlgItem返回的就是这个按钮的句柄了。

得到了句柄后,我们就需要用MoveWindow来移动这个子窗口按钮,到我们需要的位置了。第一个参数是这个字窗口的句柄,也就是我们上面获得的句柄,第二个参数和第三个参数是移动到的x,y坐标。这里我们传递buttonx和buttony,第四个和第五个是移动的窗口的长和高,如果同时还需要改变窗口的长和高,那么这里也可以传递改变后的长和高的值,我们这里只移动位置,不改变大小,所以就传递buttonWidthbuttonHeight。最后一个参数是一个BOOL型的,指示是否需要重绘,这里传递TRUE,也就是让它在移动后进行重绘。

好了,现在,当你改变主窗口的大小的时候,里面的确定按钮也会跟着改变位置而达到始终保持在主窗体的*。

可是还有一个问题就是,当我们点击按钮的时候,程序没有任何的反映,MessageBox的确定按钮被点击的时候一般都会关闭当前对话框,所以我们这里也需要实现当用户点击确定按钮的时候,将我们的主窗体关闭。那么怎么来实现呢。

在实现之前,我首先要讲一讲,子窗体是怎么通知他们的父窗体的,比如说按钮被点击的时候是怎么通知他们的父窗体的。其实一般就是通过WM_COMMAND来通知的,例如我点击这个确定按钮,那么在这个确定按钮的窗口消息处理函数中就会向它的父窗体的窗口消息处理函数发送一条WM_COMMAND消息,并且WM_COMMAND消息的wParam参数的低16包含的就是一个标识符,指示是哪个子窗体发送的这条消息。至于wParam的高16和lParam包含的是些什么信息,就请各位自己查阅MSDN了,这里我们不会用到,也就不做讲解了。

所以我们要处理在子窗体上发生的事情,就需要在父窗体的消息处理函数中截获WM_COMMAND消息,并进行处理。那么这里的WM_COMMAND消息处理也很简单,如下

	case WM_COMMAND:
		if (LOWORD(wParam) == 2)
			DestroyWindow(hwnd);
	break;

就是用LOWORD这个宏来取出wParam的低16位,并且判断是不是2,也就是判断是不是确定按钮的标识符,如果是,就表示确定按钮上发生了事件,具体的事件我们就没做过细的判断了,一般来说都是指被点击。所以我们就进行处理,调用DestroyWindow这个API来销毁主窗体。就达到了我们的目的了。

怎么样,比较简单吧~

经过7篇API入门系列文章的介绍,我想你对WIN32 API编程的一般流程还是有了一个大概的了解了吧。以及对于windows的数据类型,字符编码方面。API的使用方面,消息的处理方面,因为都不会陌生了吧·
所以我后面的文章,对于细节就不会这么细了,对于一个API函数,如果参数不是很复杂,我也不会做过多的解释了。而只是说明一下它的作用。至于细节,各位就应该养成MSDN的习惯了。

好了,今天的文章就到这里了。如果有兴趣,请加入WIN32 SDK编程群进行讨论与交流和访问我的csdn博客http://blog.csdn.net/beyondcode

我的一些其他文章也会发布到csdn的博客上面~

(附)完整程序源码

//此处省略头文件
/* BY beyondcode */
LRESULT CALLBACK WinMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
{
	//注册一个名叫MyWindowClass的窗口类
	WNDCLASSEX wc;
	wc.cbSize = sizeof(wc);
	wc.style = CS_VREDRAW | CS_HREDRAW;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WinMessageProc;
	wc.lpszMenuName = NULL;
	wc.lpszClassName = _T("MyWindowClass");
	if (!RegisterClassEx(&wc))
	{
		MessageBox(NULL, _T("注册窗口类出错"), _T("出错"), MB_OK);
		return 0;
	}
	// 根据上面注册的一个名叫MyWindowClass 的窗口类创建窗口
	HWND newWind = CreateWindowEx(0L, _T("MyWindowClass"), _T("beyondcode"), WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, NULL, NULL, hInstance, NULL);
	if (NULL == newWind)
	{
		MessageBox(NULL, _T("创建窗口出错"), _T("出错"), MB_OK);
		return 0;
	}
	ShowWindow(newWind, nShowCmd);
	UpdateWindow(newWind);
	//消息循环
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return 0;
}
LRESULT CALLBACK WinMessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	RECT rctClient,rctText; //用来存放主窗口客户区大小信息
	GetClientRect(hwnd, &rctClient); //得到主窗口客户区的大小信息
	const int buttonWidth = 80; //按钮的宽
	const int buttonHeight = 25; // 按钮的高
	const int textHeight = 25;//文字的高
	HINSTANCE hInst;
	HWND hButton;
	PAINTSTRUCT ps;
	HDC hdc;
	int buttonx, buttony;
	switch (msg){
	case WM_CREATE:
		hInst = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
		hButton = CreateWindowEx(0L, _T("button"), _T("确定"), 
			WS_VISIBLE | WS_CHILD, rctClient.right / 2 - buttonWidth / 2,
			rctClient.bottom / 2 - buttonHeight / 2, 
			buttonWidth, buttonHeight, hwnd, (HMENU)2, hInst, NULL);
		SetWindowText(hwnd, _T("自定义对话框"));
		break;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		rctText.left = rctClient.left;
		rctText.right = rctClient.right;
		rctText.top = rctClient.bottom / 2 - buttonHeight - textHeight;
		rctText.bottom = rctClient.bottom / 2 - buttonHeight;
		DrawText(hdc, _T("Beyondcode"),
			_tcslen(_T("Beyondcode")), &rctText, 
			DT_CENTER | DT_SINGLELINE | DT_VCENTER);
		EndPaint(hwnd, &ps);
		break;
	case WM_SIZE:
		GetClientRect(hwnd, &rctClient);
		buttonx = rctClient.right / 2 - buttonWidth / 2;
		buttony = rctClient.bottom / 2 - buttonHeight / 2;
		hButton = GetDlgItem(hwnd, 2);
		MoveWindow(hButton, buttonx, buttony, buttonWidth, buttonHeight, TRUE);
	break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_COMMAND:
		if (LOWORD(wParam) == 2)
			DestroyWindow(hwnd);
	break;
	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}

上一篇: 解码方法

下一篇: es学习之es入门