四、文本和输出

4.1绘制和更新
	我们已经知道,在窗口的显示区域绘制文字和图形,windows通过发送WM_PAINT消息来通知窗口显示区域需要重绘
再论WM_PAINT消息
	除了UpdateWindow调用时发送第一个WM_PAINT消息之外。窗口消息处理函数应在任何时候都准备好处理此类消息。当如下几类情况发生时,窗口过程会接收到WM_PAINT消息。
	1、移动或显示窗口,之前被隐藏的区域重新可见时。
	2、窗口大小改变(如CS_HREDRAW和CS_VREDRAW标志)
	3、使用ScrollWindow或ScrollDC函数滚动显示区域的一部分。
	4、使用InvalidateRect或InvalidateRgn刻意产生WM_PAINT消息。

有时windows试图保存显示区域以备恢复,但不一定成功,以下情况可能发送WM_PAINT消息 。
	1、windows擦除了覆盖部分窗口的对话或消息框
	2、菜单下拉出来,然后被释放
	3、显示工具提示消息。
但也有某些情况下Windows总是保存它所覆盖的显示区域然后恢复。这些情况是:
	1、鼠标光标穿越显示区。
	2、图标拖过显示区

有效矩形和无效矩形
	虽然接收WM_PAINT后要更新显示区域,但很常见的是很多时候我们只要更新一个小区域(即显示区域中的一个部分、如窗口中被对话框盖住了一小部分,对话框擦掉之后,当然也就只要更新那小块了)。这个小区域就称为无效区域(更新区)。只有显示区有部分失效时,窗口才会接收WM_PAINT消息。
	win内部为每个窗口都保留一个绘图结构,该结构包含了包围无效区最小矩形的坐标等信息,该矩形即为无效矩形(有时也称无效域)。当处理WM_PAINT消息前出现了多个无效矩形,同样将变化保存于绘图结构中,因为win不会将多个WM_PAINT消息到于队列。
	可以调用InvalidateRect使显示区域无效。通过GetUpdateRect在任何时候取得这些无效区域坐标。
	处理消息期间,如果调用了BeginPaint,则整个显示区域有效。同样调用ValidateRect使显示区内任意矩形区有效,全部有效之后,当前队列中的WM_PAINT都将被删除 
4.2 GDI简介
DC是GDI内部保存的数据结构,与特定显示设备相关。程序绘图时,首先要取得设备内容句柄。
方法一、
处理WM_PAINT时使用
PAINSTRUCT ps;  //定义于WINUSER.H头文件中
HDC hdc;
hdc=BeginPaint(hwnd,&ps);
使用GDI函数
EndPaint(hwnd,&ps);//必须成对使用
如果函数不处理WM_PAINT消息,必须传给DefWindowProc默认窗口过程,该过程中处理消息如下。
caseWM_PAINT:
BeginPaint (hwnd, &ps) ;
EndPaint (hwnd, &ps) ;
return 0 ;

不可直接返回,这是因为:如果没有BeginPaint和EndPaint(或者ValidateRect),区域会一直无效,这样导致的结果是一直发送WM_PAINT消息

PAINSTRCT结构
typedef struct tagPAINTSTRUCT
{
 HDChdc ;
 BOOLfErase ; //大多情况标志为0,表示win已擦出无效矩形背景
 RECTrcPaint ;  //定义无效矩形边界(剪取矩形)
//前三字段程序使用
 BOOLfRestore ;
 BOOLfIncUpdate ;
 BYTErgbReserved[32] ;//后三字段win内部使用
} PAINTSTRUCT ;
剪取矩形:无效区域不为矩形时,将其限制在这个矩形内。
当要在更新矩形外绘图,可以使用InvalidateRect(hwnd,NULL,TRUE),当最后参数为FALSE时,表示不擦除背景,原有东西留在原处。

方法二、
hdc=GetDC(hwnd);
使用GDI函数
ReleaseDC(hwnd,hdc);  //同样要成对使用
GetDC传回设备内容句柄有一个剪取矩形,等于整个显示区域。与BeginPaint不同的是,它不具备使显示区有效的能力。需要调用
ValidateRect(hwnd,NULL);
一般这样可以用来响应键盘或鼠标消息,因为这样可以立刻根据输入来响应而不要使用WM_PAINT消息。
 
系统字体
系统字体是一种“点阵字体”,被定义为像素块。
GetSystemMetrics:调用该函数获得使用者接口上各类视觉组件大小的信息
GetTextMetrics:取得字体大小
WINGDI.H中定义的TEXTMETRIC结构保存文字大小信息。一般有20个字段,我们使用前七个。
typedef struct tagTEXTMETRIC
{
LONG tmHeight ;
LONG tmAscent ;
LONG tmDescent ;
LONG tmInternalLeading ;
LONG tmExternalLeading ;
LONG tmAveCharWidth ;
LONG tmMaxCharWidth ;
其它结构字段
}
TEXTMETRIC, * PTEXTMETRIC ;

确定文字大小的方式,如下:
TEXTMETRIC tm;    //必须首先定义该结构变量
HDC hdc=GetDC(hwnd);    //获得设备内容句柄
GetTextMetric(hdc,&tm);
ReleaseDC(hwnd,hdc);

下面我们看一下TEXTMETRIC结构图。

tmHeight=tmAscent+tmDescent .这两个值表示了基准在线下字符的最大纵向高度。
leading指打印机在两行文字间插入的空间。
一般win启动后系统字体就不再变化。因此我们只要在合适时机调用一次函数就可以得到字体大小。显然我们可以在处理WM_CREATE时调用。如下代码:
static int cxChar,cyChar;   //静态变量,一次获取就不再改变
WM_CREATE:
TEXTMETRIC tm;
HDC hdc=GetDC();
GetTextMetrics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cyChar=tm.tmHeight+tm.tmExternalLeading;
//tmExternalLeading虽然在系统字体中为0,但是它使文字的可读性更好 ,所以加上ReleaseDC(hwnd,hdc);
return 0;

空间不够问题、
我们知道在使用程序时,有时候发现窗口尺寸变化太大。当然我们可以通过前面说的GetClientRect函数知道显示区的大小,但显然每次要知道窗口大小信息就调用效率不高。其实最好的办法是在消息处理函数中处理WM_SIZE消息。因为窗口大小发生变化时,win给窗口一个WM_SIZE消息。传入的lParam参数的低字节就包括了显示区的宽,高字节包括高度。我们可以这样处理。
static int cxClient,cyClient;
case WM_SIZE:
       cxClient=LOWORD(lParam);
       cyClient=HIWORD(lParam);
       //cyClient/cyChar   计算显示区内文字行数
       //cxClient/cxChar  水平方向显示的小写近似数
return 0;

很多程序中,一般WM_SIZE消息后面必然跟着一个WM_PAINT消息,因为我们一般定义窗口类别指定的窗口样式为 CS_HREDRAW | CS_VERDRAW //说明如果水平或ver大小改变则强制更新。
 
滚动条
内定滚动条的范围是从0(左上)到100(右下),当然也可以自定义
SetScrollRange(hwnd,iBar,iMin,iMax,bRedraw);  //具体msdn
SetScrollPos(hwnd,iBar,iPos,bRedraw);   //设置新卷动方块位置
程序写作者和win共同维护滚动条及方块位置。win要做的事情:
处理所有滚动条鼠标事件
当使用者在滚动条内单击鼠标时,提供一种「反相显示」的闪烁  //具体试下就知道,确实有闪烁现象
当使用者在滚动条内拖动卷动方块时,移动卷动方块
为包含滚动条窗口的窗口消息处理程序发送滚动条消息

程序写作者要做的事情:
       初始化位置和范围
       处理滚动条消息
       更新方块位置
       相应更新显示区域内容以响应滚动条的更改

	这时有一具问题是,当处理完滚动条消息后,程序是不会自动更新窗口的,这时我们可以用InvalidateRect函数,导致一个WM_PAINT函数置入消息队例,但同样也有一个问题,WM_PAINT消息优先级低,这样不能及时处理导致延迟,显然体验不好。为解决这个问题可以在invalidateRect之后接着调用UpdateWinow,这样就不会有延迟了。
InvalidateRect();
UpdateWinow(); 
//导致win用WM_PAINT消息调用消息处理函数,且该消息不进入消息队列,直接处理


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值