10. Windows static 控件(静态文本框控件)

使用 TextOut 和 DrawText 函数有时候会不方便,例如:

  • 文本不能自动换行,超出窗口范围会被隐藏;
  • 每次更改文本都要先擦除背景再重新输出,比较麻烦。

实际开发中一般使用静态文本框控件来输出文本。静态文本框是Windows 的一种标准控件,可以用来在窗口上显示一段文本,并且文本容易受到控制。除了静态文本框,Windows的标准控件还有很多种,例如按钮、下拉菜单、单选按钮、复选框等。

其实,**控件也是一种窗口,也使用 CreateWindow 函数来创建。**但是它们使用的窗口类的名字比较特殊,是由Windows预定义的;静态文本框控件的窗口类名是static

与前面创建的独立窗口不同,控件是子窗口,创建时必须指定父窗口,这样控件才能有“归属”。

我们先来回顾一下 CreateWindow 函数的原型:

HWND CreateWindow(
    LPCWSTR lpClassName,  //窗口类名
    LPCWSTR lpWindowName,  //窗体标题(或控件文本)
    DWORD dwStyle,  //窗口/控件样式
    int x,  //窗口相对桌面(或子窗口相对父窗口)的 X 坐标
    int y,  //窗口相对桌面(或子窗口相对父窗口)的 Y 坐标
    int nWidth,  //窗体宽度
    int nHeight,  //窗体高度
    HWND hWndParent,  //父窗口句柄
    HMENU hMenu,  //菜单句柄
    HINSTANCE hInstance,  //当前程序实例句柄
    LPVOID lpParam  //一个指向某数值的指针
);

几点说明:

  1. 对于参数 lpClassName 和 lpWindowName,一般使用宽字符,请加前缀L或使用TEXT()

    lpClassName 为窗口类的名字,可以是 RegisterClass 注册的类名,也可以是 Windows 预定义的控件类名。

    如果创建的是独立窗口,则 lpWindowName 应传入窗口的标题,若你希望创建控件,则应传入控件的文本。

  2. dwStyle 表示窗口样式或控件样式。窗口样式以 WS 开头,请查看《CreateWindow窗口风格取值》。这些样式既可以用于独立窗口,也可以用于控件(子窗口)。

    除了窗口样式,不同的控件也有自己特有的样式。对于 static 控件,它的样式以 SS 开头,常用的有:
    在这里插入图片描述

  3. 对于参数 hWndParent,如果是独立窗口,那么为 NULL,如果是控件,那么就需要父窗口的句柄。

  4. 参数 hMenu 十分重要,在后续介绍的需要处理控件消息的控件中,这是他们的唯一标识符。每个控件的 hMenu 参数值都应不同,并且需要强制转换到 HMENU 类型,如 (HMENU)1 ,再次强调,每个控件的(HMENU)后的值都应不同,可以从1往下递推。

下面的代码,会在 WM_CREATE 事件中创建 static 控件:

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE hInst;

int WINAPI WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	PSTR szCmdLine,
	int iCmdShow
) {
	static TCHAR szClassName[] = TEXT("HelloWin");   //窗口类名
	HWND hwnd;         //窗口句柄
	MSG msg;           //消息
	WNDCLASS wndclass; // 窗口类

	hInst = hInstance;

	 /**********第①步:注册窗口类**********/
	//为窗口类的各个字段赋值
	wndclass.style = CS_HREDRAW | CS_VREDRAW;	//窗口风格
	wndclass.lpfnWndProc = WndProc;		//窗口过程
	wndclass.cbClsExtra = 0;			//暂时不需要理解
	wndclass.cbWndExtra = 0;			//暂时不需要理解
	wndclass.hInstance = hInstance;			//当前窗口句柄
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);	//窗口图标
	wndclass.hCursor = LoadIcon(NULL, IDC_ARROW);		//鼠标样式
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	//窗口背景画刷
	wndclass.lpszMenuName = NULL;		//窗口菜单
	wndclass.lpszClassName = szClassName; // 窗口类名

	//注册窗口
	RegisterClass(&wndclass);


	/*****第②步:创建窗口(并让窗口显示出来)*****/
	hwnd = CreateWindow(
		szClassName,		//窗口类的名字
		TEXT("Welcome"),		//窗口标题(出现在标题栏)
		WS_OVERLAPPEDWINDOW,	//窗口风格
		CW_USEDEFAULT,			//初始化时x轴的位置
		CW_USEDEFAULT,			//初始化时y轴的位置
		500,			//窗口宽度
		300,			//窗口高度
		NULL,			//父窗口句柄
		NULL,			//窗口菜单句柄
		hInstance,		//当前窗口的句柄
		NULL			//不使用该值
	);

	//显示窗口
	ShowWindow(hwnd, iCmdShow);
	//更新(绘制)窗口
	UpdateWindow(hwnd);


	/**********第③步:消息循环**********/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

/**********第④步:窗口过程**********/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;		  //设备环境句柄
	PAINTSTRUCT ps;   //存储绘图环境的相关信息
	HWND hStatic;
	

	switch (message)
	{
	case WM_CREATE:		
		hStatic = CreateWindow(
			L"static",			//静态文本框的类名
			L"网络计算机",		//控件的文本
			WS_CHILD /*子窗口*/ | WS_VISIBLE /*创建时显示*/ | WS_BORDER /*带边框*/ | SS_CENTER /*水平居中*/ | SS_CENTERIMAGE /*垂直居中*/,
			20 /*X坐标*/, 20 /*Y坐标*/, 200 /*宽度*/, 100 /*高度*/,
			hWnd,		 //父窗口句柄
			(HMENU)1,	 //为控件指定一个唯一标识符
			hInst,		 //当前程序实例句柄
			NULL
		);
		break;

	case WM_PAINT:		//窗口绘制消息
		hdc = BeginPaint(hWnd, &ps);    //开始绘图并返回环境句柄
		// TODO:  在此添加任意绘图代码...
		EndPaint(hWnd, &ps);       //结束绘图并释放环境句柄
		break;

	case WM_DESTROY:		//窗口销毁消息
		PostQuitMessage(0);
		break;
		
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

运行效果:
在这里插入图片描述
给 CreateWindow 函数传入的第三个参数为窗口样式或控件样式(子窗口样式)。不同的控件样式一般不同,而窗口样式则大同小异:

  • WS_CHILD:表明是一个子窗口,也就是控件,不是独立窗口。
  • WS_VISIBLE:创建时显示,如果没有该样式,则需要调用ShowWindow 函数来显示。
  • WS_BORDER:带边框。

给 CreateWindow 函数传入的倒数第二个参数为 hInst,表示当前程序的实例句柄。hInst 在 WndProc 函数中并不存在,因为当前实例句柄是通过 WinMain 函数的参数传入的,所以必须要定义一个全局变量 hInst,然后在 WinMain 中给它赋值后才能使用。如下所示:

#include <windows.h>
HINSTANCE hInst;
int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    PSTR szCmdLine,
    int iCmdShow
){
    // TODO: 其他代码
    hInst = hInstance;
    // TODO: 其他代码
}

另外,你也可以通过((LPCREATESTRUCT)lParam)->hInstance语句获得当前程序实例句柄。

获取、修改控件文本

GetWindowText 函数用于将指定窗口的标题文本(如果存在)拷贝到一个缓存区内;如果指定的窗口是一个控件,则拷贝控件的文本。它的原型为:

Int GetWindowText(
    HWND hWnd,  //窗口/控件句柄
    LPTSTR lpString,  //指向接收文本的缓冲区指针
    Int nMaxCount  //要保存在缓冲区内的字符的最大个数
);

说明:

  • nMaxCount 指定要保存在缓冲区内的字符的最大个数,其中包含NULL字符。如果文本超过界限,它就被截断。
  • 函数执行成功,返回拷贝的字符的个数。

代码举例:

//定义缓冲区
TCHAR szStaticBuf[100];
//获取 static 控件的文本
GetWindowText(hStatic, szStaticBuf, 100);

类似的,SetWindowText 函数可以用来设置窗口标题或控件文本,它的原型为:

BOOL SetWindowText(
    HWND hwnd,
    LPCTSTR lpString
);

下面的例子用来显示鼠标点击的次数:

#include <Windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE hInst;

int WINAPI WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	PSTR szCmdLine,
	int iCmdShow
) {
	static TCHAR szClassName[] = TEXT("HelloWin");   //窗口类名
	HWND hwnd;         //窗口句柄
	MSG msg;           //消息
	WNDCLASS wndclass; // 窗口类

	hInst = hInstance;

	 /**********第①步:注册窗口类**********/
	//为窗口类的各个字段赋值
	wndclass.style = CS_HREDRAW | CS_VREDRAW;	//窗口风格
	wndclass.lpfnWndProc = WndProc;		//窗口过程
	wndclass.cbClsExtra = 0;			//暂时不需要理解
	wndclass.cbWndExtra = 0;			//暂时不需要理解
	wndclass.hInstance = hInstance;			//当前窗口句柄
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);	//窗口图标
	wndclass.hCursor = LoadIcon(NULL, IDC_ARROW);		//鼠标样式
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	//窗口背景画刷
	wndclass.lpszMenuName = NULL;		//窗口菜单
	wndclass.lpszClassName = szClassName; // 窗口类名

	//注册窗口
	RegisterClass(&wndclass);


	/*****第②步:创建窗口(并让窗口显示出来)*****/
	hwnd = CreateWindow(
		szClassName,		//窗口类的名字
		TEXT("Welcome"),		//窗口标题(出现在标题栏)
		WS_OVERLAPPEDWINDOW,	//窗口风格
		CW_USEDEFAULT,			//初始化时x轴的位置
		CW_USEDEFAULT,			//初始化时y轴的位置
		500,			//窗口宽度
		300,			//窗口高度
		NULL,			//父窗口句柄
		NULL,			//窗口菜单句柄
		hInstance,		//当前窗口的句柄
		NULL			//不使用该值
	);

	//显示窗口
	ShowWindow(hwnd, iCmdShow);
	//更新(绘制)窗口
	UpdateWindow(hwnd);


	/**********第③步:消息循环**********/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

/**********第④步:窗口过程**********/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;		  //设备环境句柄
	PAINTSTRUCT ps;   //存储绘图环境的相关信息
	//必须被设置为静态变量
	static int iClick = 0;		//鼠标单击次数
	static TCHAR szTextBuf[20];		//static 控件文本
	static HWND hStatic;		//static 控件句柄
	

	switch (message)
	{
	case WM_CREATE:		
		hStatic = CreateWindow(
			L"static",			//静态文本框的类名
			L"网络计算机",		//控件的文本
			WS_CHILD /*子窗口*/ | WS_VISIBLE /*创建时显示*/ | WS_BORDER /*带边框*/ | SS_CENTER /*水平居中*/ | SS_CENTERIMAGE /*垂直居中*/,
			20 /*X坐标*/, 20 /*Y坐标*/, 200 /*宽度*/, 100 /*高度*/,
			hWnd,		 //父窗口句柄
			(HMENU)1,	 //为控件指定一个唯一标识符
			hInst,		 //当前程序实例句柄
			NULL
		);
		break;

	case WM_PAINT:		//窗口绘制消息
		hdc = BeginPaint(hWnd, &ps);    //开始绘图并返回环境句柄
		// TODO:  在此添加任意绘图代码...
		EndPaint(hWnd, &ps);       //结束绘图并释放环境句柄
		break;

	case WM_LBUTTONDOWN:
		iClick++;
		wsprintf(szTextBuf, TEXT("鼠标被单击%d次"), iClick);
		SetWindowText(hStatic, szTextBuf);
		break;

	case WM_DESTROY:		//窗口销毁消息
		PostQuitMessage(0);
		break;
		
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

运行程序后,在客户区单击鼠标查看效果。下面是一张截图:
在这里插入图片描述
几点说明:

  1. WM_LBUTTONDOWN 为鼠标左键单击消息。

  2. wsprintf 与C语言中的 printf 类似,都是格式化输出函数,不过 wsprintf 一般将字符串输出到缓冲区,而 printf 输出到控制台。它的原型为:

    int wsprintf(
        LPTSTR  lpBuffer,  //接收字符串的缓冲区的指针
        LPCTSTR lpFormat,  //格式控制字符串
        [paramList]  //参数列表
    );
    
  3. 声明 static 变量的目的是使局部变量持久化。第一次执行窗口函数会产生 WM_CREATE 消息,初始化 hStatic 变量。如果不声明为 static,那么当函数执行结束后 hStatic 变量就会被销毁,接下来单击鼠标执行窗口函数时,再也不会产生 WM_CREATE 消息,也就意味着 hStatic 变量不会被赋值,将无法使用。

    对于窗口函数中的变量,如果是在 WM_CREATE 消息中赋值,但在其他消息中使用,那么一般声明为静态变量,这样下次执行窗口函数时依然有效。

  4. 还有 static 控件背景颜色和文字颜色的修改。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值