第四章:格式化文字-续

SYSMETS1.C视窗讯息处理程式
 

SYSMETS1.C程式中的WndProc视窗讯息处理程式处理三个讯息:WM_CREATE、WM_PAINT和WM_DESTROY。WM_DESTROY讯息的处理方法与第三章的HELLOWIN程式相同。

WM_CREATE讯息是视窗讯息处理程式接收到的第一个讯息。在CreateWindow函式建立视窗时,Windows产生这个讯息。在处理WM_CREATE讯息时,SYSMETS1呼叫GetDC取得视窗的装置内容,并呼叫GetTextMetrics取得内定系统字体的文字大小。SYSMETS1将平均字元宽度保存在cxChar中,将字元的总高度(包括外部间距)保存在cyChar中。

SYSMETS1还将大写字母的平均宽度保存在静态变数cxCaps中。对於固定宽度的字体, cxCaps等於cxChar。对於可变宽度字体,cxCaps设定为cxChar乘以150%。对於可变宽度字体,TEXTMETRIC结构中的tmPitchAndFamily栏位的低位元为1,对於固定宽度字体,该值为0。 SYSMETS1使用这个位元从cxChar计算cxCaps:

cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;

SYSMETS1在处理WM_PAINT讯息处理期间完成所有视窗建立工作。通常,视窗讯息处理程式先呼叫BeginPaint取得装置内容代号,然後用一道for叙述对SYSMETS.H中定义的sysmetrics结构的每一行进行回圈。三列文字用三个TextOut函式显示,对於每一列,TextOut的第三个参数都设定为:

cyChar * i

这个参数指示了字串顶端相对於显示区域顶部的图素位置。

第一条TextOut叙述在第一列显示了大写识别字。TextOut的第二个参数是0,这是说文字从显示区域的左边缘开始。文字的内容来自sysmetrics结构的szLabel栏位。我使用Windows函式lstrlen来计算字串的长度,它是TextOut需要的最後一个参数。

第二条TextOut叙述显示了对系统尺寸值的描述。这些描述存放在sysmetrics结构的szDesc栏位中。在这种情况下,TextOut的第二个参数设定为:

22 * cxCaps

第一列显示的最长的大写识别字有20个字元,因此第二列必须在第一列文字开头向右20 × cxCaps处开始。我使用22,以在两列之间加一点多余的空间。

第三条TextOut叙述显示从GetSystemMetrics函式取得的数值。变宽字体使得格式化向右对齐的数值有些棘手。从0到9的数字具有相同的宽度,但是这个宽度比空格宽度大。数值可以比一个数字宽,所以不同的数值应该从不同的横向位置开始。

那么,如果我们指定字串结束的图素位置,而不是指定字串的开始位置,以此向右对齐数值,是否会容易一些呢?用SetTextAlign函式就可以做到这一点。在SYSMETS1呼叫:

SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;

之後,传给後续TextOut函式的座标将指定字串的右上角,而不是左上角。

显示列数的TextOut函式的第二个参数设定为:

22 * cxCaps + 40 * cxChar

值40*cxChar包含了第二列的宽度和第三列的宽度。在TextOut函式之後,另一个对SetTextAlign的呼叫将对齐方式设定回普通方式,以进行下次回圈。

空间不够
 

在SYSMETS1程式中存在著一个很难处理的问题:除非您有一个大萤幕跟高解析度的显示卡,否则就无法看到系统尺度列表的最後几行。如果视窗太窄,甚至根本看不到值。

SYSMETS1不知道这个问题。否则我们就会显示一个讯息方块说「抱歉!」程式甚至不知道它的显示区域有多大,它从视窗顶部开始输出文字,并仰赖Windows裁剪超出显示区域底部的内容。

显然,这很不理想。为了解决这个问题,我们的第一个任务是确定程式在显示区域内能输出多少内容。

显示区域的大小
 

如果您使用过现有的Windows应用程式,可能会发现视窗的尺寸变化极大。视窗最大化时(假定视窗只有标题列并且没有功能表),显示区域几乎占据了整个萤幕。这一最大化了的显示区域的尺寸可以通过以SM_CXFULLSCREEN和SM_CYFULLSCREEN为参数呼叫GetSystemMetrics来获得。视窗的最小尺寸可以很小,有时甚至不存在,更不用说显示区域了。

在最近一章,我们使用GetClientRect函式来取得显示区域的大小。使用这个函式没有什么不好,但是在您每次要使用资讯时就去呼叫它一遍是没有效率的。确定视窗显示区域大小的更好方法是在视窗讯息处理程式中处理WM_SIZE讯息。在视窗大小改变时,Windows给视窗讯息处理程式发送一个WM_SIZE讯息。传给视窗讯息处理程式的lParam参数的低字组中包含显示区域的宽度,高字组中包含显示区域的高度。要保存这些尺寸,需要在视窗讯息处理程式中定义两个静态变数:

static int cxClient, cyClient ;

与cxChar和cyChar相似,这两个变数在视窗讯息处理程式内定义为静态变数,因为在以後处理其他讯息时会用到它们。处理WM_SIZE的方法如下:

case	WM_SIZE:
 	cxClient = LOWORD (lParam) ;
 	cyClient = HIWORD (lParam) ;
 	return 0 ;

实际上您会在每个Windows程式中看到类似的程式码。LOWORD和HIWORD巨集在Windows表头档案WINDEF.H中定义。这些巨集的定义看起来像这样:

#define LOWORD(l) ((WORD)(l))
#define HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))

这两个巨集传回WORD值(16位元的无正负号整数,范围从0到0xFFFF)。一般,将这些值保存在32位元有号整数中。这就不会牵扯到任何转换问题,并使得这些值在以後需要的任何计算中易於使用。

在许多Windows程式中,WM_SIZE讯息必然跟著一个WM_PAINT讯息。为什么呢?因为在我们定义视窗类别时指定视窗类别样式为:

CS_HREDRAW | CS_VREDRAW

这种视窗类别样式告诉Windows,如果水平或者垂直大小发生改变, 则强制更新显示区域。

用如下公式计算可以在显示区域内显示的文字的总行数:

cyClient / cyChar

如果显示区域的高度太小以至无法显示一个完整的字元,这个公式的结果可以为0。类似地,在显示区域的水平方向可以显示的小写字元的近似数目为:

cxClient / cxChar

如果在处理WM_CREATE讯息处理期间取得cxChar和cyChar,则不用担心在这两个计算公式中会出现被0除的情况。在WinMain呼叫CreateWindow时,视窗讯息处理程式接收一个WM_CREATE讯息。在WinMain呼叫ShowWindow之後接收到第一个WM_CREATE讯息,此时cxChar和cyChar已经被赋予正的非零值了。

如果显示区域的大小不足以容纳所有的内容,那么,知道视窗显示区域的大小只是为使用者提供了在显示区域内卷动文字的第一步。如果您对其他有类似需求的Windows应用程式很熟悉,就很可能知道,这种情况下,我们需要使用「卷动列」。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值