顯示器是由許多應(yīng)用程序填充的,所以如何合理使用這一資源是至關(guān)重要的。有兩種極端情況,一種是你的顯示區(qū)域不夠顯示,一種是夠顯示但非常的多余,資源浪費。Windows程序只能對顯示區(qū)域大小甚至字符的大小做很少的假定,必須使用Windows提供的功能來取得關(guān)于程序執(zhí)行環(huán)境的信息。關(guān)于重新繪制在書中講了許多,那是講給從dos時代走過來的人的,我用慣了xp的人,很容易明白,顯示區(qū)域是充滿意外的,我們不停移動切換著各個窗口,這時必然要重新繪制。重繪分三種情況:
繪制整個區(qū)域
在使用者移動窗口或顯示窗口時,窗口中先前被隱藏的區(qū)域重新可見。
使用者改變窗口的大小(如果窗口類別樣式有著CS_HREDRAW和CS_VREDRAW位旗標的設(shè)定)。
程序使用ScrollWindow或ScrollDC函數(shù)滾動顯示區(qū)域的一部分。
程序使用InvalidateRect或InvalidateRgn函數(shù)刻意產(chǎn)生WM_PAINT消息。
|
繪制覆蓋區(qū)域
鼠標光標穿越顯示區(qū)域。
圖標拖過顯示區(qū)域。
|
均可能發(fā)生
Windows擦除覆蓋了部分窗口的對話框或消息框。
菜單下拉出來,然后被釋放。
顯示工具提示消息
|
關(guān)于無效區(qū)域和無效矩形
無效區(qū)域是顯示器上被遮蓋的部分,這部分的圖形是可能不規(guī)則的,windows將計算出包圍這個無效區(qū)域的最小矩形,該矩形稱為無效矩形。需要強調(diào)的是消息循環(huán)中只存在一個WM_PAINT,這就要求當出現(xiàn)兩個無效區(qū)域時,windows會自動計算包圍兩個無效區(qū)域的無效矩形。窗口處理程序收到該消息時會通過GetUpdateRect來獲得坐標信息。在處理WM_PAINT消息處理期間,窗口消息處理程序在呼叫了BeginPaint之后,整個顯示區(qū)域即變?yōu)橛行А3绦蛞部梢酝ㄟ^呼叫ValidateRect函數(shù)使顯示區(qū)域內(nèi)的任意矩形區(qū)域變?yōu)橛行АW優(yōu)橛行Ъ辞宄撓ⅰ?/font>
獲得設(shè)備內(nèi)容句柄
有兩種方法獲得,第一種就是上一次講述的BeginPaint和EndPaint函數(shù)。介紹其中的PAINTSTRUCT數(shù)據(jù)結(jié)構(gòu)。
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT;
|
Windows自動填充各個屬性。我們只使用前三個屬性。HDC是設(shè)備環(huán)境句柄,由BeginPaint返回。fErase一般是0,表示windows擦除了無效矩形的背景。擦除用的畫刷就是開始窗口類中hbrBackground設(shè)定的備用畫刷。

rcPaint屬性是一個rect變量,保存著如上圖left,right,top,bottom的值,即無效矩形的邊界。想強制更新無效矩形外的區(qū)域可以使用如下函數(shù)
InvalidateRect (hwnd, NULL, TRUE) ;
|
在任何時候使用他將使整個區(qū)域變?yōu)闊o效。
另一種方法是通過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) ;
|
與上一種方法不通的是,這里需要調(diào)用ValidateRect (hWnd, NULL)使無效區(qū)域有效,如果沒有這一句,會發(fā)現(xiàn)屏幕上的字會不停的閃爍,不斷的刷新。與此類似GetWindowDC傳回寫入整個窗口的設(shè)備內(nèi)容句柄。
TextOut函數(shù)
這里需要注意的是TextOut(hdc,20,20,str,sizeof(str)/sizeof(char)-1)的最后一個參數(shù),str雖然是一個指針,但是要使用c語言的字符串
char str[]="Hello,Windows XP!";
|
若使用指針指向一個字符串,如函數(shù)中的求長度的方法將得出錯誤的結(jié)果,因為只為指針開辟了特定的空間大小,因編譯器而異。
深入字體
為了在顯示器上顯示多行文字,可以想象,必須知道字體的高度寬度等信息,這樣才不至于字與字相互覆蓋重疊。
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;
|
 通過函數(shù)可以填充這樣一個數(shù)據(jù)結(jié)構(gòu)的變量中的各個屬性。
由圖可知
TmHeight=tmAscent+tmDescent
大寫字母平均寬度=tmMaxCharWith*1.5= (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2
有了上述信息,我們就可以指定我們想要的字體了。字體的設(shè)定可以在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函數(shù),將格式化內(nèi)容放入字符數(shù)組內(nèi),該函數(shù)返回的是字符串長度,正好給TextOut函數(shù)使用。
綜合例子

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文件中,這樣會發(fā)生錯誤,因為像SM_CXSCREEN這樣的常量是在windows.H文件中的,所以如果該頭文件不包含,編譯器將提示未定義。TCHAR類型意味著要包含tchar.h。這也是原文忽略的。
system.C文件只列出消息處理函數(shù)如下
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);//右對齊方式,顯示數(shù)字
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);
}
|
當我們寫完這寫代碼時,本意味程序就可以正常運行了,但是意外發(fā)生了,
//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中,結(jié)果還是不成,會在windef或者winicon等文件中出現(xiàn)編譯錯誤。我想這一定和頭文件的版本有關(guān)。用VC2005就沒有問題了,不過在使用VC2005時會出現(xiàn)很多類型錯誤,TCHAR等類型的錯誤。
|