顯示器是由許多應(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等類型的錯誤。