-----路過的朋友,若發現錯誤或有好的建議,歡迎在下面留言,謝謝!-----
裝置描述表還真是十分重要,以至於我們在這裡還要再學習它。這節內容比較少,相信你很快就應該能看完。Let’go!
擷取裝置描述表控制代碼
複習:當你想在一個圖形輸出裝置(諸如螢幕或者印表機)上繪圖時,你首先必須獲得一個裝置描述表(或者DC)的控制代碼。將控制代碼傳回給程式時,Windows就給了你使用裝置的許可權。然後你在GDI函數中將這個控制代碼作為一個參數,向Windows標識你想在其上進行繪圖的裝置。如果在處理一個訊息時取得了裝置描述表控制代碼,應該在退出視窗函數之前釋放它(或者刪除它)。一旦釋放了控制代碼,它就不再有效了。
Windows提供了幾種取得裝置描述表控制代碼的方法,如下。
方法一:
這是擷取並釋放裝置描述表控制代碼最常用的方法,即在處理WM_PAINT訊息時使用BeginPaint和EndPaint函數。這兩個函數需要程式的視窗控制代碼和PAINTSTRUCT結構的變數的地址為參數。Windows程式員通常把這一結構變數命名為ps並且在視窗過程中定義它:
PAINTSTRUCT ps ;
在處理WM_PAINT訊息時,視窗過程首先調用BeginPaint。一般在BeginPaint函數的調用中,如果客戶區的無效地區的背景還未被擦除,則由Windows來擦除。(它使用登入的視窗類別WNDCLASS結構的hbrBackground成員變數中指定的畫刷來擦除背景,上回的注釋1我們提過)
BeginPaint調用使整個客戶區有效,並且返回一個裝置描述表控制代碼,這一傳回值通常被儲存在叫做hdc的變數中。該函數也填入ps結構的欄位(可以回憶一下上一回的注釋1)。
裝置描述表控制代碼在視窗過程中的定義如下:
HDC hdc ;//定義裝置描述表控制代碼變數
然後,程式就可以使用需要裝置描述表控制代碼的TextOut等GDI函數。在視窗的客戶區顯示文字和圖形需要裝置描述表控制代碼,但是從BeginPaint返回的裝置描述表控制代碼不能在用戶端區域之外繪圖。調用EndPaint即可釋放裝置描述表控制代碼。
一般地,處理WM_PAINT訊息的形式如下:
caseWM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
[other program lines]
EndPaint (hwnd, &ps) ;
return 0 ;
我們再來強調一下吧(別怪我囉嗦):BeginPaint調用使整個客戶區有效(阻止WM_PAINT訊息一直發送,見附錄),填充ps結構的欄位,返回的裝置描述表控制代碼。使用這個控制代碼只能在ps結構中的rcPaint欄位規定的地區內繪圖。EndPaint調用可釋放裝置描述表控制代碼。兩個函數必須成對使用,一般只用在處理WM_PAINT訊息中。
Windows程式在處理非WM_PAINT訊息時還可以用以下方法獲得裝置描述表控制代碼,當然以下函數調用不會使客戶區中的無效地區變成有效。
方法二:
hdc = GetDC (hwnd) ;
[other program lines]
ReleaseDC (hwnd, hdc) ;
GetDC函數調用後會返回hwnd參數所指定的視窗的客戶區所對應的裝置描述表控制代碼。可見GetDC調用與BeginPaint的基本區別是,利用從GetDC返回的控制代碼可以在整個客戶區上繪圖。如果hwnd參數設定為NULL,那麼函數會返回整個案頭的裝置描述表控制代碼。當不再需要該裝置環境時,需要調用ReleaseDC函數釋放裝置描述表。
方法三:
hdc = GetWindowDC (hwnd) ;
[other program lines]
ReleaseDC (hwnd, hdc) ;
GetWindowDC返回可以在整個視窗(包括客戶區部分和標題列、菜單、捲軸、架構等非客戶區部分)繪圖的裝置描述表控制代碼,不過此函數很少使用。
方法四:
hdc = CreateDC (lpszDriver, lpszDevice, lpszOutput, lpData) ;
[other program lines]
DeleteDC (hdc) ;
BeginPaint、GetDC和GetWindowDC獲得的裝置內容都與顯示器上的某個特定視窗(即hwnd)相關。CreateDC是取得裝置描述表控制代碼一個更通用的函數,它甚至可以擷取非顯示器輸出裝置描述表控制代碼。當不再需要該裝置描述表時只可調用DeleteDC函數刪除它。
當然,獲得裝置描述表的函數還有很多,我們就先簡單介紹到這兒,其他類似的函數我們以後遇到再說。
裝置描述表的屬性——我們上回提到過哦
裝置描述表中包含許多確定GDI函數如何在裝置上工作的當前“屬性”,這些屬性允許傳遞給GDI函數的參數只包含起始座標或者尺寸資訊,而不必包含Windows在裝置上顯示對象時需要的所有其它資訊。例如,上回我們調用TextOut時,我們只需要在函數中給出裝置描述表控制代碼、起始座標、文字和文字的長度。而不必指定字型、文字顏色、文字後面的背景色彩以及字元間距,因為這些屬性都是裝置描述表的一部分。當你想改變這些屬性之一時,您調用一個可以改變裝置描述表中屬性的函數(例如把文字顏色設定成紅色),以後針對該裝置描述表的TextOut調用就可以使用改變後的屬性了(例如可以輸出紅色字型)。
我在此列出如下表,方便大家查閱。
屬性 |
預設值 |
相關函數 |
背景色 |
WHITE |
GetBkColor |
SetBkColor |
背景模式 |
OPAQUE |
GetBkMode |
SetBkMode |
位元影像 |
NONE |
CreateBitMap |
CreateBitMapIndirect |
CreateCompatibleBitmap |
SelectObject |
畫刷 |
WHITE_BRUSH |
CreateBrushIndirect |
CreateDIBPatternBrush |
CreateHatchBrush |
CreatePatternBrush |
CreateSolidBrush |
SelectObject |
畫刷起始位置 |
(0,0) |
GetBrushOrg |
SetBrushOrg |
UnrealizeObject |
剪裁域 |
DISPLAY SURFACE |
ExcludeClipRect |
IntersetClipRect |
OffsetClipRgn |
SelectClipPath |
SelectObject |
SelectClipRgn |
顏色調色盤 |
DEFAULT_PALETTE |
CreatePalette |
RealizePatte |
SelectPalette |
繪圖方式 |
R2_COPYPEN |
GetROP2 |
SetROP2 |
字型 |
SYSTEM_FONT |
CreateFont |
CreateFontIndirect |
SelectObject |
字元間距 |
0 |
GetTextCharacterExtra |
SetTextCharacterExtra |
映射方式 |
MM_TEXT |
GetMapMode |
SetMapMode |
畫筆 |
BLACK_PEN |
CreatePen |
CreatePenIndirect |
SelectObject |
多邊形填充方式 |
ALTERNATE |
GetPolyFillMode |
SetPolyFileMode |
縮放模式 |
BLACKONWHITE |
SetStretchBltMode |
GetStretchBltMode |
文本顏色 |
BLACK |
GetTextColor |
SetTextColor |
視圖範圍 |
(1,1) |
GetViewportExtEx |
SetViewportExtEx |
ScaleViewportExtEx |
視圖原點 |
(0,0) |
GetViewportOrgEx |
|
|
SetViewportOrgEx |
視窗範圍 |
(1,1) |
GetWindowExtEx |
SetWindowExtEx |
ScaleWindowExtEx |
視窗原點 |
(0,0) |
GetWindowOrgEx |
OffsetWindowOrgEx |
SetWindowOrgEx |
一般以Get開頭的函數作用是擷取該屬性值,一般以Set開頭的函數作用是設定該屬性值,若有不太清楚的函數,你就自己動手查吧。
儲存裝置描述表
通常,在你調用GetDC或BeginPaint時,Windows用預設值建立一個新的裝置描述表,你對屬性所做的一切改變在裝置描述表調用ReleaseDC或EndPaint釋放時,都會丟失。如果你的程式需要使用非預設的裝置描述表屬性,則你必須在每次取得裝置描述表控制代碼時初始化裝置內容:
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
[initialize device context attributes]
[paint client area of windows]
EndPaint (hwnd, &ps) ;
return 0 ;
雖然在通常情況下這種方法已經很令人滿意了,但是你還可能想要在釋放裝置描述表之後,仍然儲存程式中對裝置描述表屬性所做的改變,以便在下一次調用GetDC和BeginPaint時它們仍然能夠起作用。為此,可在設計視窗類別時,將CS_OWNDC標誌包含為視窗類別的一部分:
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ;
現在,基於這個視窗類別所建立的每個視窗都將擁有自己的裝置描述表,它一直存在,直到視窗被刪除。如果使用了CS_OWNDC風格,就只需初始化裝置描述表一次,可以在處理WM_CREATE訊息處理期間完成這一操作:
case WM_CREATE:
hdc = GetDC (hwnd) ;
[initialize device context attributes]
ReleaseDC (hwnd, hdc) ;
這些屬性在改變之前一直有效。
CS_OWNDC風格只影響GetDC和BeginPaint獲得的裝置內容,不影響其它函數獲得的裝置描述表。使用了CS_OWNDC,你仍然應該在退出視窗過程之前釋放裝置描述表。
某些情況下,你可能想改變某些裝置描述表屬性,用改變後的屬性進行繪圖,然後恢複原來的裝置描述表屬性。要簡化這一過程,可以通過如下調用來儲存裝置描述表的狀態:
int idSaved = SaveDC (hdc) ;
現在,可以改變一些屬性,在想要回到調用SaveDC前存在的裝置內容時,調用:
RestoreDC (hdc, idSaved) ;
你可以在調用RestoreDC之前調用SaveDC數次。
大多數程式寫作者以不同的方式使用SaveDC和RestoreDC。然而,更像組合語言中的PUSH和POP指令,當你調用SaveDC時,可以不需要儲存傳回值:
SaveDC (hdc) ;
然後,你可以更改某些屬性並再次調用SaveDC。要將裝置內容恢複到一個已經儲存的狀態,調用:
RestoreDC (hdc, -1) ;
這就將裝置描述表恢複到最近由SaveDC函數儲存的狀態中。
附錄
小技巧:
在處理WM_PAINT訊息時,為了在更新的矩形外繪圖,可以在調用BeginPaint之前使用如下函數:
InvalidateRect (hwnd, NULL, TRUE) ;
它使整個顯示地區變為無效,並擦除背景。但是,如果最後一個參數等於FALSE,則不擦除背景,原有的東西將保留在原處。
通常這是Windows程式在無論何時收到WM_PAINT訊息而不考慮rcPaint結構的情況下簡單地重畫整個客戶區最方便的方法。例如,如果在客戶區的顯示輸出中包括了一個圓,但是只有圓的一部分落到了無效矩形中,它就僅繪製圓在無效矩形中的部分。這對於畫整個圓來說是無意義的。注意:使用從BeginPaint返回的裝置描述表控制代碼時,Windows不會繪製rcPaint矩形外的任何部分。在BeginPaint調用之前使用上面那個函數,可使rcPaint結構中的無效矩形為整個客戶區,就不愁繪製圓了。
在處理WM_PAINT訊息時,必須成對地調用BeginPaint和EndPaint。如果視窗過程不處理WM_PAINT訊息,則它必須將WM_PAINT訊息傳遞給Windows中DefWindowProc(預設視窗過程)。DefWindowProc以下列代碼處理WM_PAINT訊息:
case WM_PAINT:
BeginPaint (hwnd, &ps) ;
EndPaint (hwnd, &ps) ;
return 0 ;
這兩個BeginPaint和EndPaint調用之間中沒有任何語句,僅僅使先前無效地區變為有效。但以下方法是錯誤的:
case WM_PAINT:
return 0 ; // WRONG !!!
Windows將一個WM_PAINT訊息放到訊息佇列中,是因為客戶區的一部分無效。如果不調用BeginPaint和EndPaint(或者ValidateRect),則Windows不會使該地區變為有效。若一直保持無效,Windows將會再發送另一個WM_PAINT訊息,且一直發送下去。