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 //一个指向某数值的指针
);
几点说明:
- 对于参数 lpClassName 和 lpWindowName,一般使用宽字符,请加前缀L或使用TEXT()。
lpClassName 为窗口类的名字,可以是 RegisterClass 注册的类名,也可以是 Windows 预定义的控件类名。
如果你创建的是独立窗口,则 lpWindowName 应传入窗口的标题,若你希望创建控件,则应传入控件的文本。
- dwStyle 表示窗口样式或控件样式。窗口样式以 WS 开头,详情请查看《CreateWindow窗口风格取值》。这些样式既可以用于独立窗口,也可以用于控件(子窗口)。
除了窗口样式,不同的控件也有自己特有的样式。对于 static 控件,它的样式以 SS 开头,常用的有:
样式 说明
SS_LEFT 文本居左。
SS_RIGHT 文本居右。
SS_CENTER 文本居中。
SS_CENTERIMAGE 文本垂直居中。设置该样式后只能显示一行文本,即使有 ‘\n’ 也不会换行。
SS_LEFTNOWORDWRAP 文本居左,不自动换行(有 ‘\n’ 才会换行),超出控件范围的文本将被隐藏。
SS_SIMPLE 只显示一行文本(有 ‘\n’ 也不换行),且不自动换行,超出控件范围的文本将被隐藏。
3) 对于参数 hWndParent,如果是独立窗口,那么为 NULL,如果是控件,那么就需要父窗口的句柄。
- 参数 hMenu 十分重要,在后续介绍的需要处理控件消息的控件中,这是他们的唯一标识符。每个控件的 hMenu 参数值都应不同,并且需要强制转换到 HMENU 类型,如 (HMENU)1 ,再次强调,每个控件的(HMENU)后的值都应不同,可以从1往下递推。
Windows CreateFont:创建自己的字体
前面无论是使用文本输出函数还是 static 控件,字体都是默认的,比较丑陋,我们完全可以自己创建更加炫酷的字体。
创建字体使用 CreateFont 函数,它的原型是:
HFONT CreateFont(
int cHeight, //字体的逻辑高度
int cWidth, //字体的逻辑宽度
int cEscapement, //指定移位向量相对X轴的偏转角度
int cOrientation, //指定字符基线相对X轴的偏转角度
int cWeight, //设置字体粗细程度
DWORD bItalic, //是否启用斜体
DWORD bUnderline, //是否启用下划线
DWORD bStrikeOut, //是否启用删除线
DWORD iCharSet, //指定字符集
DWORD iOutPrecision, //输出精度
DWORD iClipPrecision, //剪裁精度
DWORD iQuality, //输出质量
DWORD iPitchAndFamily, //字体族
LPCSTR pszFaceName //字体名
);
上面的14个参数完全记住几乎不可能,下面是简单的说明,其他的“照猫画虎”即可。
-
参数 cWidth 和 cHeight 通常取负值,且高度为宽度的2倍,如 -18 和 -9,即可指定 13.5 |(H+W)/2| 磅的字体。
-
对于字体粗细程度 cWeight,可以使用 FW_ 开头的宏定义;一般取 FW_NORMAL (400),此参数需要值的值域为 [0,1000]。
-
对于参数 bItalic、bUnderline、bStrikeOut,直接使用 TRUE 或 FALSE 传值即可。
-
对于字符集 iCharSet,一般可以直接使用 DEFAULT_CHARSET 让系统自动处理。
注意:字体也是一种 GDI 对象,使用完后也要在 WM_DESTROY 消息中删除。
创建完字体后并不能立即使用,还需要手动触发 WM_SETFONT 消息,让Windows 将当前字体设置为我们创建的字体。
发送消息使用 SendMessage 函数,它可以让我们在必要时主动向窗口发送各种消息,原型为:
LRESULT SendMessage(
HWND hWnd, //发送消息的目标窗口句柄
UINT msg, //将要发送的消息
WPARAM wParam, //附加消息1
LPARAM lParam //附加消息2
);
这些参数和 WndProc 函数非常类似。没错,SendMessage 发送的消息就是由窗口过程来处理的。
设置字体的消息是 WM_SETFONT,只要向附加参数 wParam 传入 CreateFont 返回的句柄即可(需要转换到 WPARAM 类型)。
代码实例
下面的代码,会在 WM_CREATE 事件中创建 static 控件,并修改字体为微软雅黑:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
回调函数
参数:
hwnd : 窗口句柄
message : 消息ID
wParam和lParam:消息参数
//int i;
//size_t j;
HDC hdc;//定义设备环境句柄
static TCHAR szBuffer[128];
//TEXTMETRIC tm;
// SCROLLINFO si;
PAINTSTRUCT ps;
size_t iStrLength;
static int cxChar, cyChar, cxClient, cyClient;
static HWND hStatic;//静态文本框
static HFONT hFont; //声明一个逻辑字体句柄为静态变量
switch (message)
{
case WM_SIZE://窗体大小改变
hdc = GetDC(hWnd);
//GetClientRect(hwnd, &rect);
cxClient = LOWORD(lParam);//当前x像素
cyClient = HIWORD(lParam);//当前y像素
StringCchPrintf(szBuffer, 128, TEXT("当前客户区的分辨率:%d * %d px"), cxClient, cyClient);
StringCchLength(szBuffer, 128, &iStrLength);
SetTextAlign(hdc, TA_CENTER | TA_TOP);
TextOut(hdc, cxClient / 2, 0, szBuffer, iStrLength);
ReleaseDC(hWnd, hdc);
return 0;
case WM_CREATE:
hFont = CreateFont(
-16/*高度*/, -8/*宽度*/, 0/*不用管*/, 0/*不用管*/, 400 ,//设置字体粗细程度一般这个值设为400,
FALSE/*不带斜体*/, FALSE/*不带下划线*/, FALSE/*不带删除线*/,
DEFAULT_CHARSET, //这里我们使用默认字符集,还有其他以 _CHARSET 结尾的常量可用
OUT_CHARACTER_PRECIS, //输出精度
CLIP_CHARACTER_PRECIS, //剪裁精度
DEFAULT_QUALITY, //默认输出质量
FF_DONTCARE, //不指定字体族*/
TEXT("微软雅黑") //字体名
);
hStatic = CreateWindow(
TEXT("static"), //静态文本框的类名
TEXT("我其实是一个静态文本框"), //控件的文本
WS_CHILD /*子窗口*/ | WS_VISIBLE /*创建时显示*/ | WS_BORDER /*带边框*/ | SS_CENTER /*水平居中*/ | SS_CENTERIMAGE /*垂直居中*/,
20 /*X坐标*/, 20 /*Y坐标*/, 200 /*宽度*/, 100 /*高度*/,
hWnd, //父窗口句柄
(HMENU)1, //为控件指定一个唯一标识符
hInst, //当前程序实例句柄
NULL
);
//设置控件字体
SendMessage(
hStatic, //欲设置字体的控件句柄
WM_SETFONT, //消息名(消息类型)
(WPARAM)hFont, //字体句柄
NULL //传空值即可
);
//SendMessage(hStatic, WM_SETFONT, (WPARAM)hFont, NULL);
break;
case WM_PAINT://窗口绘画:点
{
//int x, xLast;
HDC hdc = BeginPaint(hWnd, &ps);//函数为指定窗口进行绘画作准备,并用将和绘画有关的信息填充到一个 PAINTSTRUCT 结构中。
EndPaint(hWnd, &ps);
return 0;
}
case WM_DESTROY:
//DeleteObject(hFont);
DeleteObject(hFont);//删除创建的字体
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语句获得当前程序实例句柄,有兴趣的朋友请自行Google或百度。
获取、修改控件文本
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
);
综合实例2
创建6中字体,和静态文本框,然后用户每次点击一次鼠标显示显示鼠标点击的次数,并且循环改变静态文本框内的字体显示:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
回调函数
参数:
hwnd : 窗口句柄
message : 消息ID
wParam和lParam:消息参数
//int i;
//size_t j;
HDC hdc;//定义设备环境句柄
static TCHAR szBuffer[128];
//TEXTMETRIC tm;
// SCROLLINFO si;
PAINTSTRUCT ps;
size_t iStrLength;
static int cxChar, cyChar, cxClient, cyClient;
static HWND hStatic;//静态文本框
static HFONT hFont[6]; //声明一个逻辑字体句柄为静态变量
int i,n;
//size_t iLength;
static int iClick = 0;
static TCHAR szFont[10][6] = { TEXT("微软雅黑"), TEXT("宋体"), TEXT("黑体"), TEXT("楷体"), TEXT("幼圆"), TEXT("隶书") };
switch (message)
{
case WM_SIZE://窗体大小改变
hdc = GetDC(hWnd);
//GetClientRect(hwnd, &rect);
cxClient = LOWORD(lParam);//当前x像素
cyClient = HIWORD(lParam);//当前y像素
StringCchPrintf(szBuffer, 128, TEXT("当前客户区的分辨率:%d * %d px"), cxClient, cyClient);
StringCchLength(szBuffer, 128, &iStrLength);
SetTextAlign(hdc, TA_CENTER | TA_TOP);
TextOut(hdc, cxClient / 2, 0, szBuffer, iStrLength);
ReleaseDC(hWnd, hdc);
return 0;
case WM_CREATE:
for (i = 0; i < 6; i++)
{
hFont[i] = CreateFont(
-20/*高度*/, -10/*宽度*/, 0/*不用管*/, 0/*不用管*/, 400,//设置字体粗细程度一般这个值设为400,
FALSE/*不带斜体*/, FALSE/*不带下划线*/, FALSE/*不带删除线*/,
DEFAULT_CHARSET, //这里我们使用默认字符集,还有其他以 _CHARSET 结尾的常量可用
OUT_CHARACTER_PRECIS, //输出精度
CLIP_CHARACTER_PRECIS, //剪裁精度
DEFAULT_QUALITY, //默认输出质量
FF_DONTCARE, //不指定字体族*/
szFont[i] //字体名
);
}
hStatic = CreateWindow(
TEXT("static"), //静态文本框的类名
TEXT("我其实是一个静态文本框"), //控件的文本
WS_CHILD /*子窗口*/ | WS_VISIBLE /*创建时显示*/ | WS_BORDER /*带边框*/ | SS_CENTER /*水平居中*/ | SS_CENTERIMAGE /*垂直居中*/,
20 /*X坐标*/, 20 /*Y坐标*/, 400 /*宽度*/, 300 /*高度*/,
hWnd, //父窗口句柄
(HMENU)1, //为控件指定一个唯一标识符
hInst, //当前程序实例句柄
NULL
);
break;
case WM_PAINT://窗口绘画:点
{
//int x, xLast;
HDC hdc = BeginPaint(hWnd, &ps);//函数为指定窗口进行绘画作准备,并用将和绘画有关的信息填充到一个 PAINTSTRUCT 结构中。
EndPaint(hWnd, &ps);
return 0;
}
case WM_LBUTTONDOWN: //鼠标左键单击消息
iClick++;
//设置控件字体
n = (iClick) % 6-1;
SendMessage(
hStatic, //欲设置字体的控件句柄
WM_SETFONT, //消息名(消息类型)
(WPARAM)hFont[n], //字体句柄
NULL //传空值即可
);
StringCchPrintf(szBuffer, 128, TEXT("鼠标左键被单击%d次"), iClick);
SetWindowText(hStatic, szBuffer);
break;
case WM_DESTROY:
DeleteObject(hFont);//删除创建的字体
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
显示
鼠标点击1次
点击第二次
第三个
点击第4次