显示器是由许多应用程序填充的,所以如何合理使用这一资源是至关重要的。有两种极端情况,一种是你的显示区域不够显示,一种是够显示但非常的多余,资源浪费。Windows程序只能对显示区域大小甚至字符的大小做很少的假定,必须使用Windows提供的功能来取得关于程序执行环境的信息。关于重新绘制在书中讲了许多,那是讲给从dos时代走过来的人的,我用惯了xp的人,很容易明白,显示区域是充满意外的,我们不停移动切换着各个窗口,这时必然要重新绘制。重绘分三种情况:
绘制整个区域
在使用者移动窗口或显示窗口时,窗口中先前被隐藏的区域重新可见。
使用者改变窗口的大小(如果窗口类别样式有着CS_HREDRAW和CS_VREDRAW位旗标的设定)。
程序使用ScrollWindow或ScrollDC函数滚动显示区域的一部分。
程序使用InvalidateRect或InvalidateRgn函数刻意产生WM_PAINT消息。
|
绘制覆盖区域
均可能发生
Windows擦除覆盖了部分窗口的对话框或消息框。
菜单下拉出来,然后被释放。
显示工具提示消息
|
关于无效区域和无效矩形
无效区域是显示器上被遮盖的部分,这部分的图形是可能不规则的,windows将计算出包围这个无效区域的最小矩形,该矩形称为无效矩形。需要强调的是消息循环中只存在一个WM_PAINT,这就要求当出现两个无效区域时,windows会自动计算包围两个无效区域的无效矩形。窗口处理程序收到该消息时会通过GetUpdateRect来获得坐标信息。在处理WM_PAINT消息处理期间,窗口消息处理程序在呼叫了BeginPaint之后,整个显示区域即变为有效。程序也可以通过呼叫ValidateRect函数使显示区域内的任意矩形区域变为有效。变为有效即清除该消息。
获得设备内容句柄
有两种方法获得,第一种就是上一次讲述的BeginPaint和EndPaint函数。介绍其中的PAINTSTRUCT数据结构。
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT;
|
Windows自动填充各个属性。我们只使用前三个属性。HDC是设备环境句柄,由BeginPaint返回。fErase一般是0,表示windows擦除了无效矩形的背景。擦除用的画刷就是开始窗口类中hbrBackground设定的备用画刷。
rcPaint属性是一个rect变量,保存着如上图left,right,top,bottom的值,即无效矩形的边界。想强制更新无效矩形外的区域可以使用如下函数
InvalidateRect (hwnd, NULL, TRUE) ;
|
在任何时候使用他将使整个区域变为无效。
另一种方法是通过GetDC()来获取,使用完后通过RelseaseDC()释放。
hdc=GetDC(hWnd);
GetClientRect(hWnd,&rect);
DrawText(hdc,"Hello,Windows XP!",-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
//EndPaint(hWnd,&ps);
ReleaseDC(hWnd,hdc);
ValidateRect (hWnd, NULL) ;
|
与上一种方法不通的是,这里需要调用ValidateRect (hWnd, NULL)使无效区域有效,如果没有这一句,会发现屏幕上的字会不停的闪烁,不断的刷新。与此类似GetWindowDC传回写入整个窗口的设备内容句柄。
TextOut函数
这里需要注意的是TextOut(hdc,20,20,str,sizeof(str)/sizeof(char)-1)的最后一个参数,str虽然是一个指针,但是要使用c语言的字符串
char str[]="Hello,Windows XP!";
|
若使用指针指向一个字符串,如函数中的求长度的方法将得出错误的结果,因为只为指针开辟了特定的空间大小,因编译器而异。
深入字体
为了在显示器上显示多行文字,可以想象,必须知道字体的高度宽度等信息,这样才不至于字与字相互覆盖重叠。
GetSystemMetrics
|
各类视觉组件大小
|
GetTextMetrics
|
取得字体大小
|
typedef struct tagTEXTMETRIC {
LONG tmHeight;
LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading; //纵向空隙
LONG tmExternalLeading;//横向空隙
LONG tmAveCharWidth;//小写字母宽度
LONG tmMaxCharWidth;//大写字母宽度
LONG tmWeight;
LONG tmOverhang;
LONG tmDigitizedAspectX;
LONG tmDigitizedAspectY;
char tmFirstChar;
char tmLastChar;
char tmDefaultChar;
char tmBreakChar;
BYTE tmItalic;
BYTE tmUnderlined;
BYTE tmStruckOut;
BYTE tmPitchAndFamily;
BYTE tmCharSet; } TEXTMETRIC;
|
通过函数可以填充这样一个数据结构的变量中的各个属性。
由图可知
TmHeight=tmAscent+tmDescent
大写字母平均宽度=tmMaxCharWith*1.5= (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2
有了上述信息,我们就可以指定我们想要的字体了。字体的设定可以在WM_CREATE消息处理时指定。例如
case WM_CREATE:
hdc = GetDC (hwnd) ;
GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cyChar = tm.tmHeight + tm.tmExternalLeading ;
ReleaseDC (hwnd, hdc) ;
return 0 ;
|
完成这些后就可以格式化输出了,我们需要使用wsprintf函数,将格式化内容放入字符数组内,该函数返回的是字符串长度,正好给TextOut函数使用。
综合例子
Windows.H文件
//===========================
// (c)狗尾草 2008.1.19
//===========================
#include<tchar.h>
#include<windows.h>
#define LINENUMBERS ((int)(sizeof(sysmetrics)/sizeof(sysmetrics[0])))
struct
{
int index;
TCHAR* szLable;
TCHAR* szDesc;
}
sysmetrics[]=
{
SM_CXSCREEN,"SM_CXSCREEN","窗口宽像素",
SM_CYSCREEN,"SM_CYSCREEN","窗口高像素",
SM_CXVSCROLL,"SM_CXVSCROLL","垂直滚动宽度",
SM_CYHSCROLL,"SM_CYHSCROLL","水平滚动高度",
。。。。。。
};
|
书上将windows.H文件放到system.C文件中,这样会发生错误,因为像SM_CXSCREEN这样的常量是在windows.H文件中的,所以如果该头文件不包含,编译器将提示未定义。TCHAR类型意味着要包含tchar.h。这也是原文忽略的。
system.C文件只列出消息处理函数如下
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static int cxChar,cyChar,cxCaps;
int i;
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
//char str[]="你好,Windows XP!";
TCHAR szBuffer[10];
TEXTMETRIC tm;
switch(message)
{
case WM_CREATE:
hdc=GetDC(hWnd);
GetTextMetrics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cyChar=tm.tmHeight+tm.tmExternalLeading;
cxCaps=(tm.tmPitchAndFamily&1?3:2)*cxChar/2;
ReleaseDC(hWnd,hdc);
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd,&ps);
//hdc=GetDC(hWnd);
//GetClientRect(hWnd,&rect);
//DrawText(hdc,"Hello,Windows XP!",-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
for(i=0;i<LINENUMBERS;i++)
{
TextOut(hdc,0,cyChar*i,sysmetrics[i].szLable,lstrlen(sysmetrics[i].szLable));
TextOut(hdc,20*cxCaps,cyChar*i,sysmetrics[i].szDesc,lstrlen(sysmetrics[i].szDesc));
SetTextAlign(hdc,TA_RIGHT|TA_TOP);//右对齐方式,显示数字
TextOut (hdc,22*cxCaps+40*cxChar,cyChar*i,szBuffer,wsprintf(szBuffer,TEXT("%5d"),GetSystemMetrics(sysmetrics[i].index)));
SetTextAlign(hdc,TA_LEFT|TA_TOP);//改回来哦
}
//TextOut(hdc,20,20,str,sizeof(str)/sizeof(char)-1);
EndPaint(hWnd,&ps);
//ReleaseDC(hWnd,hdc);
//ValidateRect (hWnd, NULL) ;
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,message,wParam,lParam);
}
|
当我们写完这写代码时,本意味程序就可以正常运行了,但是意外发生了,
//SM_MOUSEWHEELPRESENT,TEXT ("SM_MOUSEWHEELPRESENT"), TEXT ("Mouse wheel present flag"),
//SM_XVIRTUALSCREEN, TEXT ("SM_XVIRTUALSCREEN"), TEXT ("Virtual screen x origin"),
//SM_YVIRTUALSCREEN, TEXT ("SM_YVIRTUALSCREEN"), TEXT ("Virtual screen y origin"),
//SM_CXVIRTUALSCREEN, TEXT ("SM_CXVIRTUALSCREEN"), TEXT ("Virtual screen width"),
//SM_CYVIRTUALSCREEN, TEXT ("SM_CYVIRTUALSCREEN"),TEXT ("Virtual screen height"),
//SM_CMONITORS, TEXT ("SM_CMONITORS"), TEXT ("Number of monitors"),
//SM_SAMEDISPLAYFORMAT,TEXT ("SM_SAMEDISPLAYFORMAT"),TEXT ("Same color format flag")
|
这些常量在VC6的windows.H中居然没有包含,在用devcpp编译,OK,非常成功的通过了,我拷贝devcpp中的头文件到VC6中,结果还是不成,会在windef或者winicon等文件中出现编译错误。我想这一定和头文件的版本有关。用VC2005就没有问题了,不过在使用VC2005时会出现很多类型错误,TCHAR等类型的错误。
|