📄 4.输出文字.txt
字号:
字符大小
要用TextOut显示多行文字,就必须确定字体的字符大小,可以根据字符的高度来定位字符的后续行,以及根据字符的宽度来定位字符的后续列。
系统字体的字符高度和平均宽度是多少?这个问题取决于视讯显示器的图素大小。Windows需要的最小显示大小是640×480,但是许多使用者更喜欢800×600或1024×768的显示大小。另外,对于这些较大的显示尺寸,Windows允许使用者选择不同大小的系统字体。
程序可以呼叫GetSystemMetrics函数以取使用者接口上各类视觉组件大小的信息,呼叫GetTextMetrics取得字体大小。GetTextMetrics传回设备内容中目前选取的字体信息,因此它需要设备内容句柄。Windows将文字大小的不同值复制到在WINGDI.H中定义的TEXTMETRIC型态的结构中。TEXTMETRIC结构有20个字段,我们只使用前七个:
typedef struct tagTEXTMETRIC
{
LONG tmHeight ;
LONG tmAscent ;
LONG tmDescent ;
LONG tmInternalLeading ;
LONG tmExternalLeading ;
LONG tmAveCharWidth ;
LONG tmMaxCharWidth ;
其它结构字段
}
TEXTMETRIC, * PTEXTMETRIC ;
这些字段值的单位取决于选定的设备内容映像方式。在内定设备内容下,映像方式是MM_TEXT,因此值的大小是以图素为单位。
要使用GetTextMetrics函数,需要先定义一个结构变量(通常称为tm):
TEXTMETRIC tm ;
在需要确定文字大小时,先取得设备内容句柄,再呼叫GetTextMetrics:
hdc = GetDC (hwnd) ;
GetTextMetrics (hdc, &tm) ;
ReleaseDC (hwnd, hdc) ;
此后,您就可以查看文字尺寸结构中的值,并有可能保存其中的一些以备将来使用。
文字大小:细节
TEXTMETRIC结构提供了关于目前设备内容中选用的字体的丰富信息。但是,字体的纵向大小只由5个值确定,其中4个值如图4-3所示。
图4-3 定义字体中纵向字符大小的4个值
最重要的值是tmHeight,它是tmAscent和tmDescent的和。这两个值表示了基准在线下字符的最大纵向高度。「间距」(leading)指打印机在两行文字间插入的空间。在TEXTMETRIC结构中,内部的间距包括在tmAscent中(因此也在tmHeight中),并且它经常是重音符号出现的地方。tmInternalLeading字段可被设成0,在这种情况下,加重音的字母会稍稍缩短以便容纳重音符号。
TEXTMETRIC结构还包括一个不包含在tmHeight值中的字段tmExternalLeading。它是字体设计者建议加在横向字符之间的空间大小。在安排文字行之间的空隙时,您可以接受设计者建议的值,也可以拒绝它。在系统字体中tmExternalLeading可以为0,因此我没有在图4-3中显示它。(尽管我不想告诉你们,图4-3确实就是Windows在640×480的显示分辨率中使用的系统字体。)
TEXTMETRICS结构包含有描述字符宽度的两个字段,即tmAveCharWidth(小写字母加权平均宽度)和tmMaxCharWidth(字体中最宽字符的宽度)。对于定宽字体,这两个值是相等的(图4-3中这些值分别为7和14)。
本章的范例程序还需要另一种字符宽度,即大写字母的平均宽度,这可以用tmAveCharWidth乘以150%大致计算出来。
必须认识到,系统字体的大小取决于Windows所执行的视讯显示器的分辨率,在某些情况下,取决于使用者选取的系统字体的大小。Windows提供了一个与设备无关的图形接口,但程序写作者还是有事情要处理的。不要想当然耳地猜测字体大小来写作Windows程序,也不要把值定死,您可以使用GetTextMetrics函数取得这一信息。
格式化文字
Windows启动后,系统字体的大小就不会发生改变,所以在程序执行过程中,程序写作者只需要呼叫一次GetTexMetrics。最好是在窗口消息处理程序中处理WM_CREATE消息时进行此呼叫,WM_CREATE消息是窗口消息处理程序接收的第一个消息。在WinMain中呼叫CreateWindow时,Windows会以一个WM_CREATE消息呼叫窗口消息处理程序。
假设要编写一个Windows程序,在显示区域显示几行文字,这需要先取得字符宽度和高度。您可以在窗口消息处理程序内定义两个变量来保存平均字符宽度(cxChar)和总的字符高度(cyChar):
static int cxChar, cyChar ;
变量名的前缀c代表「count」,在这里指图素数,与x和y结合,分别指宽和高。这些变量定义为static静态变量,因为它们在窗口消息处理程序中处理其它消息(如WM_PAINT)时也应该是有效的。如果变量在函数外面定义,则不需要定义为static。
下面是取得系统字体的字符宽度和高度的WM_CREATE程序代码:
case WM_CREATE:
hdc = GetDC (hwnd) ;
GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cyChar = tm.tmHeight + tm.tmExternalLeading ;
ReleaseDC (hwnd, hdc) ;
return 0 ;
注意我在计算cyChar时包括了tmExternalLeading字段,虽然该字段在系统字体中为0,但是因为它使得文字的可读性更好,所以还是应该把它包括进去。沿着窗口向下每隔cyChar图素就会显示一行文字。
您会发现常常需要显示格式化的数字跟简单的字符串。我在第二章讲到过,您不能使惯用的工具(可爱的printf函数)来完成这项工作,但是可以使用sprintf和Windows版的sprintf-wsprintf。这些函数与printf相似,只是把格式化字符串放到字符串中。然后,可以用TextOut将字符串输出到显示器上。非常方便的是,从sprintf和wsprintf传回的值就是字符串的长度。您可以将这个值传递给TextOut作为iLength参数。下面的程序代码显示了wsprintf与TextOut的典型组合:
int iLength ;
TCHAR szBuffer [40] ;
其它行程序
iLength = wsprintf (szBuffer, TEXT ("The sum of %i and %i is %i"),
iA, iB, iA + iB) ;
TextOut (hdc, x, y, szBuffer, iLength) ;
对于这样简单的情况,可以将nLength的定义值与TextOut放在同一条叙述中,从而无需定义iLength:
TextOut (hdc, x, y, szBuffer,
wsprintf (szBuffer, TEXT ("The sum of %i and %i is %i"),
iA, iB, iA + iB)) ;
虽然这样子写起来不好看,但是功能与前者是一样的。
综合使用
现在,我们似乎已经具备了在屏幕上显示多行文字所需要的所有知识。我们知道如何在WM_PAINT消息处理期间取得一个设备内容句柄,如何使用TextOut函数以及如何根据字符大小来安排字距,剩下的就是显示一点有意义的东西了。
在前一章里,我们大概知道从Windows的GetSystemMetrics函数中取得的信息是很有意义的,该函数传回Windows中不同视觉组件的大小信息,如图标、光标、标题列和滚动条等。它们的大小因显示卡和驱动程序的不同而有所不同。GetSystemMetrics是在程序中完成与设备无关图形输出的重要函数。
该函数需要一个参数,叫做「索引」,在Windows表头文件定义了75个整数索引标识符(标识符的数量随着每个版本的Windows的发布而不断地增加,在Windows 1.0的程序写作者文件中仅列出了26个)。GetSystemMetrics传回一个整数,这个整数通常就是参数中指定的图形组件大小。
让我们来编写一个程序,显示一些可以从GetSystemMetrics呼叫中取得的信息,显示格式为每种视觉组件一行。如果我们建立一个表头文件,在表头文件中定义一个结构数组,此结构包含GetSystemMetrics索引对应的Windows表头文件标识符和呼叫所传回的每个值对应的字符串,这样处理起来要容易一些。表头文件名为SYSMETS.H,如程序4-1所示。
程序4-1 SYSMETS.H
/*---------------------------------------------------------
SYSMETS.H -- System metrics display structure
-----------------------------------------------------------*/
#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))
struct
{
int Index ;
TCHAR * szLabel ;
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"),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -