本讲中要使用TextOut函数来在窗口上显示多行文字,首先介绍TextOut函数的用法。
TextOut函数在指定位置写入的字符串,利用当前选择的字体,背景颜色和文本颜色。
BOOL TextOutA(
HDC hdc,
int x,
int y,
LPCSTR lpString,
int c
);
第一个参数hdc为装置内容代号,可以是GetDC的回传值,也可以是在处理WM_PAINT信息的BeginPaint回传值。
第二个参数x和第三个参数y为系统对齐字符的参考点,原点为显示区域的左上角。
第四个参数lpString为指向字符串的指针。
第五个参数c为字符串的长度。
如果想要在视窗中显示多个字符,那么就必须知道字符的大小,在早期的windows版本中,使用的字体为等宽字体即所有的字符都具有相等的宽度,从windows3.0之后,windows的系统字体变成了变宽字体,如大写W字符要比小写的a字符宽。windows的系统字体是一种点阵字体。
字元大小
对于不同的分辨率,会有不同的大小的系统字体,可以通过GetTextMetrics
获得字体大小的信息,windows对于不同字体的信息存放在TEXTMETRIC 结构体中
typedef struct tagTEXTMETRICA {
LONG tmHeight;
LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading;
LONG tmExternalLeading;
LONG tmAveCharWidth;
LONG tmMaxCharWidth;
LONG tmWeight;
LONG tmOverhang;
LONG tmDigitizedAspectX;
LONG tmDigitizedAspectY;
BYTE tmFirstChar;
BYTE tmLastChar;
BYTE tmDefaultChar;
BYTE tmBreakChar;
BYTE tmItalic;
BYTE tmUnderlined;
BYTE tmStruckOut;
BYTE tmPitchAndFamily;
BYTE tmCharSet;
} TEXTMETRICA, *PTEXTMETRICA, *NPTEXTMETRICA, *LPTEXTMETRICA;
第一个参数tmHeight为字符的高度(tmAscent+tmDescent 上升+下降)。
第二个参数tmAscent为字符的上升(基线上方的单位)。
第三个参数tmDescent为字符的下降(基线下方的单位)。
第四个参数tmInternalLeading为tmHeight成员设置的边界内的前导(空格)量,通常是用来标注重音,可以设置为0。
第五个参数tmExternalLeading为它是字体设计者建议加在横向字元之间的空间大小,可以将其设为0。
第六个参数tmAveCharWidth为字体中字符的平均宽度(通常定义为字母x的宽度)。此值不包括粗体或斜体字符所需的突出部分。对于大写字母的平均宽度则需要tmAveCharWidth的1.5倍。
第七个参数tmMaxCharWidth为字体中最宽字符的宽度。
格式化文字
如果要在视窗中显示字体,就必须首先取得字元的高度和宽度,可以在开始定义两个变量来保存平均字元的宽度和总的字元高度,如下:
cxChar = tm.tmAveCharWidth ;
cyChar = tm.tmHeight + tm.tmExternalLeading ;
可以看到在计算cyChar时包括了tmExternalLeading 栏位来确保可读性。
要显示格式化的字符串必须要使用sprintf和wsprintf函数来显示,从 sprintf 和 wsprintf 传回的值就是字串的长度。
综合使用
/*-------------------------------------
SYSMETS.H -- System metrics display structure
---------------------------------------*/
#include <tchar.h>
#include <Windows.h>
#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))
struct
{
int index;
TCHAR * szLable;
TCHAR * szDesc;
}
sysmetrics[] = {
SM_CXSCREEN, TEXT("SM_CXSCREEN"), TEXT("Screen width in pixels"),
SM_CYSCREEN, TEXT("SM_CYSCREEN"),TEXT("Screen height in pixels"),
SM_CXVSCROLL, TEXT("SM_CXVSCROLL"), TEXT("Vertical scroll width"),
SM_CYHSCROLL, TEXT("SM_CYHSCROLL"), TEXT("Horizontal scroll height"),
SM_CYCAPTION, TEXT("SM_CYCAPTION"), TEXT("Caption bar height"),
SM_CXBORDER, TEXT("SM_CXBORDER"), TEXT("Window border width"),
SM_CYBORDER, TEXT("SM_CYBORDER"), TEXT("Window border height"),
SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"), TEXT("Dialog window frame width"),
SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"), TEXT("Dialog window frame height"),
SM_CYVTHUMB, TEXT("SM_CYVTHUMB"), TEXT("Vertical scroll thumb height"),
SM_CXHTHUMB, TEXT("SM_CXHTHUMB"), TEXT("Horizontal scroll thumb width"),
SM_CXICON, TEXT("SM_CXICON"), TEXT("Icon width"),
SM_CYICON, TEXT("SM_CYICON"), TEXT("Icon height"),
SM_CXCURSOR, TEXT("SM_CXCURSOR"), TEXT("Cursor width"),
SM_CYCURSOR, TEXT("SM_CYCURSOR"), TEXT("Cursor height"),
SM_CYMENU, TEXT("SM_CYMENU"), TEXT("Menu bar height"),
SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"), TEXT("Full screen client area width"),
SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"), TEXT("Full screen client area height"),
SM_CYKANJIWINDOW, TEXT("SM_CYKANJIWINDOW"), TEXT("Kanji window height"),
SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"), TEXT("Mouse present flag"),
SM_CYVSCROLL, TEXT("SM_CYVSCROLL"), TEXT("Vertical scroll arrow height"),
SM_CXHSCROLL, TEXT("SM_CXHSCROLL"), TEXT("Horizontal scroll arrow width"),
SM_DEBUG, TEXT("SM_DEBUG"), TEXT("Debug version flag"),
SM_SWAPBUTTON, TEXT("SM_SWAPBUTTON"), TEXT("Mouse buttons swapped flag"),
SM_CXMIN, TEXT("SM_CXMIN"), TEXT("Minimum window width"),
SM_CYMIN, TEXT("SM_CYMIN"), TEXT("Minimum window height"),
SM_CXSIZE, TEXT("SM_CXSIZE"), TEXT("Min/Max/Close button width"),
SM_CYSIZE, TEXT("SM_CYSIZE"), TEXT("Min/Max/Close button height"),
SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"), TEXT("Window sizing frame width"),
SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"), TEXT("Window sizing frame height"),
SM_CXMINTRACK, TEXT("SM_CXMINTRACK"), TEXT("Minimum window tracking width"),
SM_CYMINTRACK, TEXT("SM_CYMINTRACK"), TEXT("Minimum window tracking height"),
SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"), TEXT("Double click x tolerance"),
SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"), TEXT("Double click y tolerance"),
SM_CXICONSPACING,TEXT("SM_CXICONSPACING"), TEXT("Horizontal icon spacing"),
SM_CYICONSPACING,TEXT("SM_CYICONSPACING"), TEXT("Vertical icon spacing"),
SM_MENUDROPALIGNMENT, TEXT("SM_MENUDROPALIGNMENT"), TEXT("Left or right menu drop"),
SM_PENWINDOWS, TEXT("SM_PENWINDOWS"), TEXT("Pen extensions installed"),
SM_DBCSENABLED, TEXT("SM_DBCSENABLED"), TEXT("Double-Byte Char Set enabled"),
SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"), TEXT("Number of mouse buttons"),
SM_SECURE, TEXT("SM_SECURE"), TEXT("Security present flag"),
SM_CXEDGE, TEXT("SM_CXEDGE"), TEXT("3-D border width"),
SM_CYEDGE, TEXT("SM_CYEDGE"), TEXT("3-D border height"),
};
/*-------------------------------------
将SYSMETS.H中的结构体显示到视窗上面
---------------------------------------*/
#include<Windows.h>
#include "SYSTEMS.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR sCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("SysMets1");
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(wndclass); //结构的大小(必须定义结构大小)
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 = ::LoadCursor(NULL, IDC_ARROW); //使用预定义光标
wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);//使用白色背景画刷
wndclass.lpszMenuName = NULL; //不指定菜单
wndclass.lpszClassName = szAppName; //窗口类的名称
wndclass.hIconSm = NULL; //没有类的小图标
if (!RegisterClassEx(&wndclass)) {
MessageBox(NULL, TEXT("Can't funcitom"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindowEx(
0, //dwExStyle, 扩展样式
szAppName, //lpClassName,类名
"WindowsName", //lpWindowName窗口名称
WS_OVERLAPPEDWINDOW, //dwStyle, 窗口风格
CW_USEDEFAULT, //X,初始X坐标
CW_USEDEFAULT, //Y,初始Y坐标
CW_USEDEFAULT, //nWight, 宽度
CW_USEDEFAULT, //nHight, 高度
NULL, //hWndParent, 父窗口句柄
NULL, //hMenu, 菜单句柄
hInstance, //hInstance, 程序实例句柄
NULL //ipParam,用户数据
);
::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)
{
static int cxChar, cxCaps, cyChar;
HDC hdc;
int i;
PAINTSTRUCT ps;
TCHAR szBuff[10];
TEXTMETRIC tm;
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2)* cxChar / 2;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
for (int i = 0; i < NUMLINES; i++) {
TextOut(hdc, 0, cyChar * i, sysmetrics[i].szLable,
lstrlen(sysmetrics[i].szLable));
TextOut(hdc, 22 * cxCaps, cyChar * i, sysmetrics[i].szDesc,
lstrlen(sysmetrics[i].szDesc));
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, 22 * cxCaps + 40 * cxChar, cyChar * i, szBuff, wsprintf(szBuff, TEXT("%5d"),
GetSystemMetrics(sysmetrics[i].index)));
SetTextAlign(hdc, TA_LEFT | TA_TOP);
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
该程序是将一个结构体数组中的全部内容打印在视窗中,该数组存储再SYSTEMS.h的头文件中,对与视窗的创建和前面所说的完全一致,重点放在视窗信息处理函数的处理过程上。在信息处理函数中要处理三个命令。分别是WM_CREATE、 WM_PAINT和WM_DESTROY。
在创建视窗时,我们会先执行第一个讯息WM_CREATE,首先通过GetDC函数获取该视窗的装置代号并存储在hdc 变量中,并且调用GetTextMetrics 函数获得系统中字体的一些信息,将系统字体的平均宽度存储在cxChar中,字元的总高度存储在cyChar中 , 还将大写字母的平均宽度保存在静态变量 cxCaps,对于固定宽度的字体,cxCaps等于 cxChar。最后调用ReleaseDC释放当前装置代号。
在处理WM_PAINT信息时,首先使用BeginPaint获取装置内容代号,之后使用for循环将结构体数组中内容全部打出,使用TextOut函数。可以看到在三个TextOut函数中每次循环的y坐标均相同,为cyChar * i,x坐标依次向后,并且长度是固定的,所以最终显示的应该是三列显示出,在计算字符串长度时,使用lstrlen函数来确定长度。
参考资料:
[1]《Windows程序设计(第五版)》
[2]https://docs.microsoft.com/zh-cn/welcome-to-docs