MFC自繪UI你離不開GDI繪圖
GDI (Graphics Device Interface)是圖形設(shè)備接口的英文縮寫,處理Windows程序的圖形和圖像輸出。程序員不需要關(guān)心硬件設(shè)備及設(shè)備驅(qū)動(dòng),就可以將應(yīng)用程序的輸出轉(zhuǎn)換為硬件設(shè)備上的輸出,實(shí)現(xiàn)應(yīng)用程序與硬件設(shè)備的隔離,大大簡(jiǎn)化程序開發(fā)工作。在Windows操作系統(tǒng)中,圖形界面應(yīng)用程序通常離不開GDI,利用GDI所提供的眾多函數(shù)可以方便地在屏幕、打印機(jī)以及其他輸出設(shè)備上實(shí)現(xiàn)輸出文本、圖形等操作。
設(shè)備環(huán)境(DC)
設(shè)備無關(guān)性(也稱設(shè)備獨(dú)立性)是Windows的主要功能之一。應(yīng)用程序可以在各種設(shè)備上進(jìn)行繪制和打印輸出,系統(tǒng)統(tǒng)一把所有外部設(shè)備都當(dāng)作文件來看待,只要安裝了它們的驅(qū)動(dòng)程序,應(yīng)用程序就可以像使用文件一樣操縱、使用這些設(shè)備,GDI代表應(yīng)用程序和設(shè)備驅(qū)動(dòng)程序進(jìn)行交互。為了實(shí)現(xiàn)設(shè)備無關(guān)性,引入了邏輯設(shè)備和物理設(shè)備這兩個(gè)概念,在應(yīng)用程序中,使用邏輯設(shè)備名稱來請(qǐng)求使用某類設(shè)備,而系統(tǒng)在實(shí)際執(zhí)行時(shí),使用的是物理設(shè)備名稱。設(shè)備無關(guān)性的支持包含在兩個(gè)動(dòng)態(tài)鏈接庫(kù)中,第一個(gè)是GDI相關(guān)動(dòng)態(tài)鏈接庫(kù),稱為圖形設(shè)備接口﹔第二個(gè)是設(shè)備驅(qū)動(dòng)程序,設(shè)備驅(qū)動(dòng)程序的名稱取決于應(yīng)用程序繪制輸出的設(shè)備。GDI處理程序的繪圖酗數(shù)調(diào)用,將這些調(diào)用傳遞給設(shè)備驅(qū)動(dòng)程序,設(shè)備驅(qū)動(dòng)程序接收來自GDI的輸入,將輸入轉(zhuǎn)換為設(shè)備命今,并將這些命今傳遞給對(duì)應(yīng)的設(shè)備。
當(dāng)程序在客戶區(qū)中顯示文本或圖形時(shí),我們通常稱程序在“繪制"客戶區(qū)。GDI在加載驅(qū)動(dòng)程序后,準(zhǔn)備設(shè)備進(jìn)行繪制操作,例如選擇線條顏色和寬度、畫刷顏色和圖案、字體名稱、裁剪區(qū)域等。這些任務(wù)是通過創(chuàng)建和維護(hù)設(shè)備環(huán)境(DC)來完成的。DC是定義一組圖形對(duì)象及其關(guān)聯(lián)屬性以及影響輸出的圖形模式的結(jié)構(gòu)體。
與DC相關(guān)的部分圖形對(duì)象及屬性如下表所示。
圖形對(duì)象屬性
-
畫筆樣式、寬度和顏色
-
畫刷樣式、顏色、圖案和原點(diǎn)
-
字體字體名稱、字體大小、字符寬度、字符高度、字符集等
-
位圖大小(以字節(jié)為單位),尺寸(以像素為單位)、顏色格式、壓縮方案等
-
路徑形狀
-
區(qū)域位置和尺寸
與大多數(shù)結(jié)構(gòu)體不同,應(yīng)用程序不能直接訪問DC,而是通過調(diào)用各種函數(shù)間接地對(duì)DC結(jié)構(gòu)進(jìn)行操作。
Windows支持5種圖形模式,允許應(yīng)用程序指定顏色的混合方式、輸出的位置、輸出的縮放方式等。下表描述了存儲(chǔ)在DC中的這些模式。
圖形模式描述
- 背景模式文本的背景色與現(xiàn)有窗口或屏幕顏色的混合方式等
- 繪圖模式畫筆、畫刷的顏色與目標(biāo)顯示區(qū)域顏色的混合方式等
- 映射模式如何將圖形輸出從邏輯坐標(biāo)映射到客戶區(qū)、屏幕或打印機(jī)紙張
- 多邊形填充模式如何使用畫刷填充復(fù)雜區(qū)域的內(nèi)部
- 拉伸模式當(dāng)位圖被放大或縮小時(shí)如何計(jì)算新位圖
Windows有4種類型的DC,分別是顯示設(shè)備DC、打印DC、內(nèi)存DC(也稱內(nèi)存兼容DC)、信息DC,每種類型的DC都有特定的用途,如下表所述。
DC類型描述
- 顯示設(shè)備DC在顯示器上進(jìn)行繪圖操作
- 打印DC在打印機(jī)或繪圖儀上進(jìn)行繪圖操作
- 內(nèi)存DC通常是在內(nèi)存中的位圖上進(jìn)行繪圖操作
- 信息DC獲取設(shè)備環(huán)境信息
也就是說,通過設(shè)備環(huán)境,不僅可以在屏幕窗口進(jìn)行繪圖,也可以在打印機(jī)或繪圖儀上進(jìn)行繪圖,還可以在內(nèi)存中的位圖上進(jìn)行繪圖。關(guān)于圖形對(duì)象、圖形模式以及各種DC類型,后面會(huì)分別進(jìn)行詳細(xì)介紹。
獲取顯示設(shè)備DC句柄
DC句柄是程序使用GDI函數(shù)的通行證,幾乎所有的GDI繪圖函數(shù)都需要一個(gè)DC句柄參數(shù),有了DC句柄,便能隨心所欲地繪制窗口客戶區(qū)。
前面說過,當(dāng)窗口客戶區(qū)的部分或全部變?yōu)?無效"且必須"更新"時(shí),比如說改變窗口大小、最小化/最大化窗口、拖動(dòng)窗口一部分到屏幕外再拖動(dòng)回來時(shí),應(yīng)用程序?qū)?huì)獲取到WM_PAINT消息。窗口過程的大部分繪圖操作是在處理WM_PAINT消息期間進(jìn)行的,可以通過調(diào)用BeginPaint函數(shù)來獲取顯示DC句柄。WM_PAINT消息的處理邏輯一般如下∶
HDC hdc;//顯示設(shè)備DC句柄
PAINTSTRUCT ps;//繪圖結(jié)構(gòu)體
hdc = ::BeginPaint(hwnd, &ps);
//TODO:在這里開始你的 繪圖代碼 編寫
::EndPaint(hwnd, &ps);
BeginPaint函數(shù)的返回值就是需要更新區(qū)域的DC句柄hdc。
BeginPaint返回的hdc對(duì)應(yīng)的尺寸僅是無效區(qū)域,程序無法通過該句柄繪制到這個(gè)區(qū)域以外的地方。由于窗口過程每次接收到WM_PAINT消息時(shí)的無效區(qū)域可能不同,因此這個(gè)hdc值僅在當(dāng)次WM_PAINT消息中有效,程序不應(yīng)該保存它并把它,用在WM_PAINT消息以外的代碼中。BeginPaint和EndPaint函數(shù)只能用在WM_PAINT消息中,因?yàn)橹挥羞@時(shí)才存在無效區(qū)域。BeginPaint函數(shù)還有一個(gè)作用就是把無效區(qū)域有效化,如果不調(diào)用BeginPaint,那么窗口的無效區(qū)域就一直不為空,系統(tǒng)會(huì)一直發(fā)送WM PAINT消息。
窗口客戶區(qū)中存在一個(gè)無效區(qū)域,這將導(dǎo)致Windows在應(yīng)用程序的消息隊(duì)列中放置一條WM_PAINT消息,即只有當(dāng)程序客戶區(qū)的一部分或全部無效時(shí),窗口過程才會(huì)接收到WM_PATNT消息。Windows在內(nèi)部為每個(gè)窗口都保存了一個(gè)繪制信息結(jié)構(gòu)PAINTSTRUCT,這個(gè)結(jié)構(gòu)保存著一個(gè)可以覆蓋該無效區(qū)域的最小矩形的坐標(biāo)和一些其他信息,這個(gè)最小矩形稱為無效矩形。如果在窗口過程處理一條WM_PAINT消息之前,窗口客戶區(qū)中又出現(xiàn)了另一個(gè)無效區(qū)域,那么Windows將計(jì)算出一個(gè)可以覆蓋這兩個(gè)無效區(qū)域的新的無效區(qū)域,并更新
PAINTSTRUCT結(jié)構(gòu)。Windows不會(huì)在消息隊(duì)列中放置多條WM_PAINT消息。
WM_PAINT消息是一個(gè)低優(yōu)先級(jí)的消息,Windows總是在消息循環(huán)為空的時(shí)候孑把WM_PAINT消息放入消息隊(duì)列。每當(dāng)消息循環(huán)為空的時(shí)候,如果Windows發(fā)現(xiàn)存在一個(gè)無效區(qū)域,就會(huì)在程序的消息隊(duì)列中放入一個(gè)WM_PAINT消息。前面說過“當(dāng)程序窗口被首次創(chuàng)建時(shí),整個(gè)客戶區(qū)都是無效的",因?yàn)榇藭r(shí)應(yīng)用程序尚未在該窗口上繪制任何東西。在WinMain中調(diào)用UpdateWindow函數(shù)時(shí)會(huì)發(fā)送第一條
wM_PAINT消息,指示窗口過程在窗口客戶區(qū)進(jìn)行繪制,
UpdateWindow函數(shù)將WM_PAINT消息直接發(fā)送到指定窗口的窗口過程,繞過應(yīng)用程序的消息隊(duì)列?,F(xiàn)在大家應(yīng)該明白,
UpdateWindow函數(shù)只不過是讓窗口過程盡快更新窗口,
HelloWindows程序去掉UpdateWindow函數(shù)調(diào)用也可以正常運(yùn)行。
如果應(yīng)用程序在其他任何時(shí)間(例如在處理鍵盤或鼠標(biāo)消息期間)需要進(jìn)行繪制,可以調(diào)用GetDC或GetDCEx函數(shù)來獲取顯示DC句柄∶
hdc = GetDC(hwnd);
//繪圖代碼
ReleaseDC(hwnd, hdc);
GetDC函數(shù)返回的hdc對(duì)應(yīng)指定窗口的整個(gè)客戶區(qū),通過GetDC 函數(shù)返回的hdc可以在客戶區(qū)的任何位置進(jìn)行繪制操作,不存在無效矩形的概念,無效矩形和BeginPaint才是原配。當(dāng)使用完畢時(shí),必須調(diào)用ReleaseDC函數(shù)釋放DC。對(duì)于用GetDC獲取的hdc,Windows建議使用的范圍限于單條消息內(nèi)。當(dāng)程序處理某條消息的時(shí)候,如果需要繪制客戶區(qū),可以調(diào)用GetDC函數(shù)獲取hdc,但在消息返回前,必須調(diào)用ReleaseDC函數(shù)將它釋放掉。如果在下一條消息中還需要用到hdc,那么可以重新調(diào)用GetDC函數(shù)獲取。如果將GetDC的hwnd參數(shù)設(shè)置為NULL,那么函數(shù)獲取的是整個(gè)屏幕的DC句柄。
現(xiàn)在我提出一個(gè)問題,相信讀者是可以理解的,按下鼠標(biāo)左鍵時(shí)將會(huì)產(chǎn)生WM_LBUTTONDOWN消息,鼠標(biāo)在客戶區(qū)中移動(dòng)的時(shí)候會(huì)不斷產(chǎn)生WM_MOUSEMOVE消息,這兩個(gè)消息的IParam參數(shù)中都含有鼠標(biāo)坐標(biāo)信息。按住鼠標(biāo)左鍵不放拖動(dòng)鼠標(biāo)會(huì)產(chǎn)生WM_LBUTTONDOWN消息和一系列WM_MOUSEMOVE消息,我們?cè)诖翱谶^程中處理WM_LBUTTONDOWN和WM_MOUSEMOVE消息,利用GetDC函數(shù)獲取DC句柄進(jìn)行繪圖,連接
WM_LBUTTONDOWN消息和一系列WM_MOUSEMOVE消息的這些坐標(biāo)點(diǎn)就會(huì)形成一條線,但是當(dāng)改變窗口大小、最小化然后最大化窗口、拖動(dòng)窗口一部分到屏幕外再拖回來時(shí),讀者會(huì)發(fā)現(xiàn)這條線沒有了,因?yàn)樵谛枰乩L的時(shí)候Windows會(huì)使用指定的背景畫刷擦除背景。如果希望這條線繼續(xù)存在,就必須在WM_PAINT消息中重新繪制(可以事先保存好那些點(diǎn))。如果可能,我們最好是在WM_PAINT消息中處理所有繪制工作。
GetWindowDC函數(shù)可以獲取整個(gè)窗口的DC句柄,包括非客戶區(qū)(例如標(biāo)題欄、菜單和滾動(dòng)條)。使用GetWindowDC函數(shù)返回的hdc可以在窗口的任何位置進(jìn)行繪制,因?yàn)檫@個(gè)DC的原點(diǎn)是窗口的左上角,而不是窗口客戶區(qū)的左上角,例如,程序可以使用
GetWindowDC函數(shù)返回的hdc在窗口的標(biāo)題欄上進(jìn)行繪制,這時(shí)程序需要處理WM_NCPAINT(非客戶區(qū)繪制)消息。
HDC GetWindowDC( _In_ HWND hWnd);
函數(shù)執(zhí)行成功,返回值是指定窗口的DC句柄。同樣的,完成繪制后必須調(diào)用ReleaseDC函數(shù)來釋放DC。如果將參數(shù)hWnd參數(shù)設(shè)置為NULL,GetWindowDC函數(shù)獲取的是整個(gè)屏幕的DC句柄。
Windows有4種類型的DC,關(guān)于其他類型DC句柄的獲取,后面用到的時(shí)候再講解。理論知識(shí)講解太多實(shí)在乏味,接下來先實(shí)現(xiàn)一個(gè)輸出(繪制)文本的示例,并實(shí)現(xiàn)滾動(dòng)條功能。
繪制文本
GetSystemMetrics函數(shù)用于獲取系統(tǒng)度量或系統(tǒng)配置信息,例如可以獲取屏幕分辨率、全屏窗口客戶區(qū)的寬度和高度、滾動(dòng)條的寬度和高度等,該函數(shù)獲取到的相關(guān)度量信息均以像素為單位∶
int WINAPI GetSystemMetrics(_In_ int nlndex);
該函數(shù)只有一個(gè)參數(shù),稱之為索引,這個(gè)索引有95個(gè)標(biāo)識(shí)符可以使用。
例如GetSystemMetrics(SM_CXSCREEN)獲取的是屏幕的寬度(Cx表示 Count X,X軸像素?cái)?shù)), SystemMetrics程序根據(jù)95個(gè)索引在客戶區(qū)中輸出95行,每行的格式類似下面的樣子∶
SM_CXSCREEN 屏幕的寬度 1366
通過TextOut函數(shù)輸出METRICS結(jié)構(gòu)數(shù)組的每個(gè)數(shù)組元素很簡(jiǎn)單。這里列舉下筆者的實(shí)現(xiàn)。
- Metrics.h
#pragma once
struct
{
int m_nIndex;
PTSTR m_pLabel;
PTSTR m_pDesc;
}METRICS[] = {
SM_CXSCREEN, TEXT("SM_CXSCREEN"), TEXT("屏幕的寬度"),
SM_CYSCREEN, TEXT("SM_CYSCREEN"), TEXT("屏幕的高度"),
SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"), TEXT("全屏窗口的客戶區(qū)寬度"),
SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"), TEXT("全屏窗口的客戶區(qū)高度"),
SM_ARRANGE, TEXT("SM_ARRANGE"), TEXT("如何排列最小化窗口"),
SM_CLEANBOOT, TEXT("SM_CLEANBOOT"), TEXT("系統(tǒng)啟動(dòng)方式"),
SM_CMONITORS, TEXT("SM_CMONITORS"), TEXT("監(jiān)視器的數(shù)量"),
SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"), TEXT("鼠標(biāo)上的按鈕數(shù)"),
SM_CONVERTIBLESLATEMODE, TEXT("SM_CONVERTIBLESLATEMODE"), TEXT("筆記本電腦或平板電腦模式"),
SM_CXBORDER, TEXT("SM_CXBORDER"), TEXT("窗口邊框的寬度"),
SM_CYBORDER, TEXT("SM_CYBORDER"), TEXT("窗口邊框的高度"),
SM_CXCURSOR, TEXT("SM_CXCURSOR"), TEXT("光標(biāo)的寬度"),
SM_CYCURSOR, TEXT("SM_CYCURSOR"), TEXT("光標(biāo)的高度"),
SM_CXDLGFRAME, TEXT("SM_CXDLGFRAME"), TEXT("同SM_CXFIXEDFRAME,有標(biāo)題但不可調(diào)整大小的窗口邊框的寬度"),
SM_CYDLGFRAME, TEXT("SM_CYDLGFRAME"), TEXT("同SM_CYFIXEDFRAME,有標(biāo)題但不可調(diào)整大小的窗口邊框的高度"),
SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"), TEXT("鼠標(biāo)雙擊事件兩次點(diǎn)擊的X坐標(biāo)不可以超過這個(gè)值"),
SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"), TEXT("鼠標(biāo)雙擊事件兩次點(diǎn)擊的Y坐標(biāo)不可以超過這個(gè)值"),
SM_CXDRAG, TEXT("SM_CXDRAG"), TEXT("拖動(dòng)操作開始之前,鼠標(biāo)指針可以移動(dòng)的鼠標(biāo)下方點(diǎn)的任意一側(cè)的像素?cái)?shù)"),
SM_CYDRAG, TEXT("SM_CYDRAG"), TEXT("拖動(dòng)操作開始之前,鼠標(biāo)指針可以移動(dòng)的鼠標(biāo)下移點(diǎn)上方和下方的像素?cái)?shù)"),
SM_CXEDGE, TEXT("SM_CXEDGE"), TEXT("三維邊框的寬度"),
SM_CYEDGE, TEXT("SM_CYEDGE"), TEXT("三維邊框的高度"),
SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"), TEXT("同SM_CXDLGFRAME,有標(biāo)題但不可調(diào)整大小的窗口邊框的寬度"),
SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"), TEXT("同SM_CYDLGFRAME,有標(biāo)題但不可調(diào)整大小的窗口邊框的高度"),
SM_CXFOCUSBORDER, TEXT("SM_CXFOCUSBORDER"), TEXT("DrawFocusRect繪制的焦點(diǎn)矩形的左邊緣和右邊緣的寬度"),
SM_CYFOCUSBORDER, TEXT("SM_CYFOCUSBORDER"), TEXT("DrawFocusRect繪制的焦點(diǎn)矩形的上邊緣和下邊緣的高度"),
SM_CXFRAME, TEXT("SM_CXFRAME"), TEXT("同SM_CXSIZEFRAME,可調(diào)大小窗口邊框的寬度"),
SM_CYFRAME, TEXT("SM_CYFRAME"), TEXT("同SM_CYSIZEFRAME,可調(diào)大小窗口邊框的高度"),
SM_CXHSCROLL, TEXT("SM_CXHSCROLL"), TEXT("水平滾動(dòng)條中箭頭位圖的寬度"),
SM_CYHSCROLL, TEXT("SM_CYHSCROLL"), TEXT("水平滾動(dòng)條中箭頭位圖的高度"),
SM_CXVSCROLL, TEXT("SM_CXVSCROLL"), TEXT("垂直滾動(dòng)條中箭頭位圖的寬度"),
SM_CYVSCROLL, TEXT("SM_CYVSCROLL"), TEXT("垂直滾動(dòng)條中箭頭位圖的高度"),
SM_CXHTHUMB, TEXT("SM_CXHTHUMB"), TEXT("水平滾動(dòng)條中滾動(dòng)框(滑塊)的高度"),
SM_CYVTHUMB, TEXT("SM_CYVTHUMB"), TEXT("垂直滾動(dòng)條中滾動(dòng)框(滑塊)的寬度"),
SM_CXICON, TEXT("SM_CXICON"), TEXT("圖標(biāo)的默認(rèn)寬度"),
SM_CYICON, TEXT("SM_CYICON"), TEXT("圖標(biāo)的默認(rèn)高度"),
SM_CXICONSPACING, TEXT("SM_CXICONSPACING"), TEXT("大圖標(biāo)視圖中項(xiàng)目的網(wǎng)格單元格寬度"),
SM_CYICONSPACING, TEXT("SM_CYICONSPACING"), TEXT("大圖標(biāo)視圖中項(xiàng)目的網(wǎng)格單元格高度"),
SM_CXMAXIMIZED, TEXT("SM_CXMAXIMIZED"), TEXT("最大化頂層窗口的默認(rèn)寬度"),
SM_CYMAXIMIZED, TEXT("SM_CYMAXIMIZED"), TEXT("最大化頂層窗口的默認(rèn)高度"),
SM_CXMAXTRACK, TEXT("SM_CXMAXTRACK"), TEXT("具有標(biāo)題和大小調(diào)整邊框的窗口可以拖動(dòng)的最大寬度"),
SM_CYMAXTRACK, TEXT("SM_CYMAXTRACK"), TEXT("具有標(biāo)題和大小調(diào)整邊框的窗口可以拖動(dòng)的最大高度"),
SM_CXMENUCHECK, TEXT("SM_CXMENUCHECK"), TEXT("菜單項(xiàng)前面復(fù)選框位圖的寬度"),
SM_CYMENUCHECK, TEXT("SM_CYMENUCHECK"), TEXT("菜單項(xiàng)前面復(fù)選框位圖的高度"),
SM_CXMENUSIZE, TEXT("SM_CXMENUSIZE"), TEXT("菜單欄按鈕的寬度"),
SM_CYMENUSIZE, TEXT("SM_CYMENUSIZE"), TEXT("菜單欄按鈕的高度"),
SM_CXMIN, TEXT("SM_CXMIN"), TEXT("窗口的最小寬度"),
SM_CYMIN, TEXT("SM_CYMIN"), TEXT("窗口的最小高度"),
SM_CXMINIMIZED, TEXT("SM_CXMINIMIZED"), TEXT("最小化窗口的寬度"),
SM_CYMINIMIZED, TEXT("SM_CYMINIMIZED"), TEXT("最小化窗口的高度"),
SM_CXMINSPACING, TEXT("SM_CXMINSPACING"), TEXT("最小化窗口的網(wǎng)格單元寬度"),
SM_CYMINSPACING, TEXT("SM_CYMINSPACING"), TEXT("最小化窗口的網(wǎng)格單元高度"),
SM_CXMINTRACK, TEXT("SM_CXMINTRACK"), TEXT("窗口的最小拖動(dòng)寬度,用戶無法將窗口拖動(dòng)到小于這些尺寸"),
SM_CYMINTRACK, TEXT("SM_CYMINTRACK"), TEXT("窗口的最小拖動(dòng)高度,用戶無法將窗口拖動(dòng)到小于這些尺寸"),
SM_CXPADDEDBORDER, TEXT("SM_CXPADDEDBORDER"), TEXT("標(biāo)題窗口的邊框填充量"),
SM_CXSIZE, TEXT("SM_CXSIZE"), TEXT("窗口標(biāo)題或標(biāo)題欄中按鈕的寬度"),
SM_CYSIZE, TEXT("SM_CYSIZE"), TEXT("窗口標(biāo)題或標(biāo)題欄中按鈕的高度"),
SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"), TEXT("同SM_CXFRAME,可調(diào)大小窗口邊框的寬度"),
SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"), TEXT("同SM_CYFRAME,可調(diào)大小窗口邊框的厚度"),
SM_CXSMICON, TEXT("SM_CXSMICON"), TEXT("小圖標(biāo)的建議寬度"),
SM_CYSMICON, TEXT("SM_CYSMICON"), TEXT("小圖標(biāo)的建議高度"),
SM_CXSMSIZE, TEXT("SM_CXSMSIZE"), TEXT("小標(biāo)題按鈕的寬度"),
SM_CYSMSIZE, TEXT("SM_CYSMSIZE"), TEXT("小標(biāo)題按鈕的高度"),
SM_CXVIRTUALSCREEN, TEXT("SM_CXVIRTUALSCREEN"), TEXT("虛擬屏幕的寬度"),
SM_CYVIRTUALSCREEN, TEXT("SM_CYVIRTUALSCREEN"), TEXT("虛擬屏幕的高度"),
SM_CYCAPTION, TEXT("SM_CYCAPTION"), TEXT("標(biāo)題區(qū)域的高度"),
SM_CYKANJIWINDOW, TEXT("SM_CYKANJIWINDOW"), TEXT("屏幕底部的日文漢字窗口的高度"),
SM_CYMENU, TEXT("SM_CYMENU"), TEXT("單行菜單欄的高度"),
SM_CYSMCAPTION, TEXT("SM_CYSMCAPTION"), TEXT("小標(biāo)題的高度"),
SM_DBCSENABLED, TEXT("SM_DBCSENABLED"), TEXT("User32.dll是否支持DBCS"),
SM_DEBUG, TEXT("SM_DEBUG"), TEXT("是否安裝了User.exe的調(diào)試版本"),
SM_DIGITIZER, TEXT("SM_DIGITIZER"), TEXT("設(shè)備支持的數(shù)字轉(zhuǎn)換器輸入類型"),
SM_IMMENABLED, TEXT("SM_IMMENABLED"), TEXT("是否啟用了輸入法管理器/輸入法編輯器功能"),
SM_MAXIMUMTOUCHES, TEXT("SM_MAXIMUMTOUCHES"), TEXT("系統(tǒng)中是否有數(shù)字化儀"),
SM_MEDIACENTER, TEXT("SM_MEDIACENTER"), TEXT("當(dāng)前操作系統(tǒng)是不是Windows XP Media Center"),
SM_MENUDROPALIGNMENT, TEXT("SM_MENUDROPALIGNMENT"), TEXT("下拉菜單是否與相應(yīng)的菜單欄項(xiàng)右對(duì)齊"),
SM_MIDEASTENABLED, TEXT("SM_MIDEASTENABLED"), TEXT("系統(tǒng)是否啟用希伯來語(yǔ)和阿拉伯語(yǔ)"),
SM_MOUSEHORIZONTALWHEELPRESENT, TEXT("SM_MOUSEHORIZONTALWHEELPRESENT"), TEXT("是否安裝了帶有水平滾輪的鼠標(biāo)"),
SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"), TEXT("是否安裝了鼠標(biāo)"),
SM_MOUSEWHEELPRESENT, TEXT("SM_MOUSEWHEELPRESENT"), TEXT("是否安裝了帶有垂直滾輪的鼠標(biāo)"),
SM_NETWORK, TEXT("SM_NETWORK"), TEXT("是否存在網(wǎng)絡(luò)"),
SM_PENWINDOWS, TEXT("SM_PENWINDOWS"), TEXT("是否安裝了Microsoft Windows for Pen Computing擴(kuò)展"),
SM_REMOTECONTROL, TEXT("SM_REMOTECONTROL"), TEXT("當(dāng)前終端服務(wù)器會(huì)話是否被遠(yuǎn)程控制"),
SM_REMOTESESSION, TEXT("SM_REMOTESESSION"), TEXT("調(diào)用進(jìn)程是否與終端服務(wù)客戶機(jī)會(huì)話關(guān)聯(lián)"),
SM_SAMEDISPLAYFORMAT, TEXT("SM_SAMEDISPLAYFORMAT"), TEXT("所有顯示器的顏色格式是否相同"),
SM_SECURE, TEXT("SM_SECURE"), TEXT("始終返回0"),
SM_SERVERR2, TEXT("SM_SERVERR2"), TEXT("系統(tǒng)是否是Windows Server 2003 R2"),
SM_SHOWSOUNDS, TEXT("SM_SHOWSOUNDS"), TEXT("用戶是否要求應(yīng)用程序在其他情況下以可視方式呈現(xiàn)信息"),
SM_SHUTTINGDOWN, TEXT("SM_SHUTTINGDOWN"), TEXT("當(dāng)前會(huì)話是否正在關(guān)閉"),
SM_SLOWMACHINE, TEXT("SM_SLOWMACHINE"), TEXT("計(jì)算機(jī)是否具有低端(慢速)處理器"),
SM_STARTER, TEXT("SM_STARTER"), TEXT("當(dāng)前操作系統(tǒng)版本"),
SM_SWAPBUTTON, TEXT("SM_SWAPBUTTON"), TEXT("鼠標(biāo)左鍵和右鍵的功能是否互換了"),
SM_SYSTEMDOCKED, TEXT("SM_SYSTEMDOCKED"), TEXT("??磕J降臓顟B(tài)"),
SM_TABLETPC, TEXT("SM_TABLETPC"), TEXT("是否啟動(dòng)了Tablet PC輸入服務(wù)"),
SM_XVIRTUALSCREEN, TEXT("SM_XVIRTUALSCREEN"), TEXT("虛擬屏幕左側(cè)的坐標(biāo)"),
SM_YVIRTUALSCREEN, TEXT("SM_YVIRTUALSCREEN"), TEXT("虛擬屏幕頂部的坐標(biāo)")
};
- SystemMetrics.cpp
#include <Windows.h>
#include <tchar.h>
#include "Metrics.h"
const int NUMLINES = sizeof(METRICS) / sizeof(METRICS[0]); //寫入文本的行數(shù)
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wndclass; // RegisterClassEx函數(shù)用的WNDCLASSEX結(jié)構(gòu)
TCHAR szClassName[] = TEXT("MyWindow"); // RegisterClassEx函數(shù)注冊(cè)的窗口類的名稱
TCHAR szAppName[] = TEXT("GetSystemMetrics"); // 窗口標(biāo)題
HWND hwnd; // CreateWindowEx函數(shù)創(chuàng)建的窗口的句柄
MSG msg; // 消息循環(huán)所用的消息結(jié)構(gòu)體
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // 窗口背景使用標(biāo)準(zhǔn)系統(tǒng)顏色
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szClassName;
wndclass.hIconSm = NULL;
::RegisterClassEx(&wndclass);
hwnd = ::CreateWindowEx(0, szClassName, szAppName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
::ShowWindow(hwnd, nCmdShow);
::UpdateWindow(hwnd);
while (::GetMessage(&msg, NULL, 0, 0) != 0)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TCHAR szBuf[10];
int y;
if (uMsg == WM_PAINT)
{
hdc = ::BeginPaint(hwnd, &ps);
for (int i = 0; i < NUMLINES; i++)
{
y = 18 * i; //行間距
::TextOut(hdc, 0, y, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
::TextOut(hdc, 240, y, METRICS[i].m_pDesc, _tcslen(METRICS[i].m_pDesc));
::TextOut(hdc, 760, y, szBuf,
wsprintf(szBuf, TEXT("%d"), ::GetSystemMetrics(METRICS[i].m_nIndex)));
}
::EndPaint(hwnd, &ps);
return 0;
}
else if (uMsg == WM_DESTROY)
{
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
程序使用wndclass.hbrBackground =(HBRUSH)(COLOR_BTNFACE+ 1);
把窗口背景設(shè)置為標(biāo)準(zhǔn)系統(tǒng)顏色(淺灰色),所以很容易發(fā)現(xiàn)文本其實(shí)是有背景色的,默認(rèn)是白色背景﹔字體是系統(tǒng)字體(System字體,標(biāo)題欄、菜單、對(duì)話框默認(rèn)情況下使用系統(tǒng)字體);對(duì)于每一行的行間距以及每一列的距離,我們大體設(shè)置了一個(gè)數(shù)值,這并不準(zhǔn)確;客戶區(qū)一共輸出了95行,但是由于屏幕分辨率的原因,無法完整顯示出來,很明顯程序需要一個(gè)垂直滾動(dòng)條。
格式化文本
文本輸出是程序客戶區(qū)中最常見的圖形輸出類型,有一些函數(shù)可以格式化和繪制文本。
格式化函數(shù)可以設(shè)置背景模式、背景顏色、對(duì)齊方式、文本顏色、字符間距等,這些都是DC的文本格式屬性。背景模式不透明、背景顏色為白色、對(duì)齊方式為左對(duì)齊、文本顏色為黑色等都是默認(rèn)的DC文本格式屬性。
格式函數(shù)可以分為三類∶
- 獲取或設(shè)置DC的文本格式屬性的函數(shù)
- 獲取字符寬度和高度的函數(shù)
- 獲取字符串寬度和高度的函數(shù)。
文本格式屬性
- 文本對(duì)齊方式
SetTextAlign函數(shù)為指定的DC設(shè)置文本對(duì)齊方式∶
UINT SetTextAlign
(
_In_HDC hdc, //設(shè)備環(huán)境句柄
_In_ UINT fMode //文本對(duì)齊方式
);
fMode參數(shù)指定文本對(duì)齊方式,可用的值及含義如下表所示。
常量 | 常量含義 |
---|---|
TA_TOP |
起始點(diǎn)在文本邊界矩形的上邊緣 |
TA_BOTTOM |
起始點(diǎn)在文本邊界矩形的下邊緣 |
TA_BASELINE |
起始點(diǎn)在文本的基線上 |
TA_LEFT |
起始點(diǎn)在文本邊界矩形的左邊緣 |
TA_RIGHT |
起始點(diǎn)在文本邊界矩形的右邊緣 |
TA_CENTER |
起始點(diǎn)在文本邊界矩形的中心(水平方向) |
TA_UPDATECP |
使用當(dāng)前位置作為起始點(diǎn),當(dāng)前位置在每次文本輸出函數(shù)調(diào)用后會(huì)更新 |
TA_NOUPDATECP |
每次文本輸出函數(shù)調(diào)用以后,當(dāng)前位置不會(huì)更新 |
默認(rèn)值為TA_LEFT|TA_TOP |TA_NOUPDATECP.
調(diào)用SetTextAlign函數(shù)可以改變TextOutExtTextOut.TabbedTextOut等函數(shù)中nXStart和nYStart參數(shù)表示的含義
-
使用
TA_LEFT
TA_RIGHT
和TA_CENTER
標(biāo)志會(huì)影響nXStart
表示的水平坐標(biāo)值。 -
使用
TA_TOP
TA_BOTTOM
和TA_BASELINE
標(biāo)志會(huì)影響nYStart
表示的垂直坐標(biāo)值。
例如在SetTextAlign函數(shù)中指定TA_RIGHT標(biāo)志,那么TextOut函數(shù)的nXStart表示字符串中最后一個(gè)字符右側(cè)的水平坐標(biāo)。如果指定TA_TOP,則nYStart表示字符串中所有字符的最高點(diǎn),即所有字符都在nYStart指定的位置之下﹔如果指定TA_BOTTOM則表示字符串中所有字符都會(huì)在nYStart指定的位置之上。
如果設(shè)置了TA_UPDATECP標(biāo)志,Windows會(huì)忽略TextOut函數(shù)的nXStart和nYStart參數(shù)指定的值,而是將由先前調(diào)用的MoveToEx或LineTo函數(shù)(或其他一些可以改變當(dāng)前位置的函數(shù))指定的當(dāng)前位置坐標(biāo)值作為起始點(diǎn)。
如果沒有調(diào)用改變當(dāng)前位置的函數(shù),那么默認(rèn)情況下當(dāng)前位置的坐標(biāo)為(0,0),相對(duì)于客戶區(qū)左上角;設(shè)置
TA_UPDATECP標(biāo)志以后,對(duì)TextOut函數(shù)的每次調(diào)用也會(huì)更新當(dāng)前位置。
例如,如果設(shè)置為TA_LEFT|TA_UPDATECP,TextOut函數(shù)返回后新的當(dāng)前位置就是該字符串的結(jié)束位置,下次調(diào)用TextOut函數(shù)時(shí)就會(huì)從上一個(gè)字符串的結(jié)束位置開始繪制,有時(shí)候可能需要這個(gè)特性。
如果函數(shù)執(zhí)行成功,則返回值是原來的文本對(duì)齊設(shè)置 如果函數(shù)執(zhí)行失敗,則返回值為GDI_ERROR.
大家可以把SystemMetrics程序的最后一個(gè)TextOut改為∶
#include <Windows.h>
#include <tchar.h>
#include "Metrics.h"
const int NUMLINES = sizeof(METRICS) / sizeof(METRICS[0]); //寫入文本的行數(shù)
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wndclass; // RegisterClassEx函數(shù)用的WNDCLASSEX結(jié)構(gòu)
TCHAR szClassName[] = TEXT("MyWindow"); // RegisterClassEx函數(shù)注冊(cè)的窗口類的名稱
TCHAR szAppName[] = TEXT("GetSystemMetrics"); // 窗口標(biāo)題
HWND hwnd; // CreateWindowEx函數(shù)創(chuàng)建的窗口的句柄
MSG msg; // 消息循環(huán)所用的消息結(jié)構(gòu)體
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // 窗口背景使用標(biāo)準(zhǔn)系統(tǒng)顏色
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szClassName;
wndclass.hIconSm = NULL;
::RegisterClassEx(&wndclass);
hwnd = ::CreateWindowEx(0, szClassName, szAppName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
::ShowWindow(hwnd, nCmdShow);
::UpdateWindow(hwnd);
while (::GetMessage(&msg, NULL, 0, 0) != 0)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TCHAR szBuf[10];
int y;
if (uMsg == WM_PAINT)
{
hdc = ::BeginPaint(hwnd, &ps);
for (int i = 0; i < NUMLINES; i++)
{
y = 18 * i; //行間距
::TextOut(hdc, 0, y, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
::TextOut(hdc, 240, y, METRICS[i].m_pDesc, _tcslen(METRICS[i].m_pDesc));
//設(shè)置最后一列右對(duì)齊
::SetTextAlign(hdc, TA_RIGHT | TA_TOP | TA_NOUPDATECP);
::TextOut(hdc, 760, y, szBuf, wsprintf(szBuf, TEXT("%d"), ::GetSystemMetrics(METRICS[i].m_nIndex)));
//渲染完畢之后,恢復(fù)預(yù)設(shè)
::SetTextAlign(hdc, TA_LEFT | TA_TOP | TA_NOUPDATECP);
}
::EndPaint(hwnd, &ps);
return 0;
}
else if (uMsg == WM_DESTROY)
{
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
將fMode參數(shù)設(shè)置為TA_RIGHT,那么TextOut的nXStart參數(shù)指定的就是字符串中最后一個(gè)字符右側(cè)的X坐標(biāo)。
可以看到數(shù)據(jù)從右開始變得對(duì)齊了。
可以通過調(diào)用GetTextAlign函數(shù)來獲取指定DC的當(dāng)前文本對(duì)齊設(shè)置∶
UINT GetTextAlign(_In_ HDC hdc);
調(diào)用SetTextAlign函數(shù)的時(shí)候通常使用按位或運(yùn)算符組合幾個(gè)標(biāo)志,調(diào)用GetTextAlign函數(shù)的時(shí)候可以使用按位”與"運(yùn)算符檢測(cè)返回值是否包含某標(biāo)志。
- 字符間距
可以通過調(diào)用SetTextCharacterExtra函數(shù)設(shè)置指定DC中文本輸出的字符間距︰
int SetTextCharacterExtra
(
HDC hdc, //設(shè)備環(huán)境句柄
int nCharExtra //字符間距,邏輯單位
);
大家可以在SystemMetrics程序的3個(gè)TextOut前調(diào)用SetTextCharacterExtra函數(shù)設(shè)置一下字符間距,看一下效果,比如說:
//設(shè)置字符寬度
::SetTextCharacterExtra(hdc, 5);
::TextOut(hdc, 760, y, szBuf, wsprintf(szBuf, TEXT("%d"), ::GetSystemMetrics(METRICS[i].m_nIndex)));
//恢復(fù)預(yù)設(shè)
::SetTextCharacterExtra(hdc, 0);
可以通過調(diào)用GetTextCharacterExtra函數(shù)來獲取指定DC的當(dāng)前字符間距∶
int GetTextCharacterExtra(HDC hdc);
- 背景模式、背景顏色和文本顏色
可以通過調(diào)用SetTextColor函數(shù)設(shè)置繪制的文本顏色,以及在彩色打印機(jī)上繪制的文本顏色;
可以通過調(diào)用SetBkColor函數(shù)設(shè)置每個(gè)字符后顯示的顏色(也就是背景顏色);
可以通過調(diào)用SetBkMode函數(shù)設(shè)置背景模式為透明或不透明。
COLORREF SetTextColor
(
HDC hdc, //設(shè)備環(huán)境句柄
COLORREF crColor //文本顏色值
);
如果函數(shù)執(zhí)行成功,則返回原來的背景顏色值;如果函數(shù)執(zhí)行失敗,則返回值為CLR_INVALID.
int SetBkMode(
HDC hdc, //設(shè)備環(huán)境句柄
int iBkMode //背景模式
);
iBkMode參數(shù)指定背景模式,可用的值只有兩個(gè)∶指定為OPAQUE表示不透明背景,指定為TRANSPARENT表示透明背景。
如果函數(shù)執(zhí)行成功,則返回原來的的背景模式﹔如果函數(shù)執(zhí)行失敗,則返回值為0。
COLORREF用于指定RGB顏色值,在windef.h頭文件中定義如下∶
typedef DWORD COLORREF;
typedef DWORD *LPCOLORREF;
COLORREF值的十六進(jìn)制為"Ox00BBGGRR"的形式,低位字節(jié)包含紅色值,倒數(shù)第2字節(jié)包含綠色值,倒數(shù)第3字節(jié)包含藍(lán)色值,高位字節(jié)必須為0,單字節(jié)的最大值為255.
要?jiǎng)?chuàng)建COLORREF顏色值,可以使用RGB宏分別指定紅色、綠色、藍(lán)色的值;
要提取COLORREF顏色值中的的紅色、綠色和藍(lán)色值,可以分別使用GetRValue GetGValue和GetBValue宏。
這些宏在wingdi.h頭文件中定義如下∶
#define RGB(r,g,b)
((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(DWORD)(BYTE)(b))<<16)))
#define GetRValue(rgb)(LOBYTE(rgb))
#define GetGValue(rgb)(LOBYTE((WORD)(rgb)) >>8))
#define GetBValue(rgb)(LOBYTE((rgb)>>16))
現(xiàn)在,我們?cè)赟ystemMetrics程序的TextOut函數(shù)調(diào)用前面加上以下語(yǔ)句︰
#include <Windows.h>
#include <tchar.h>
#include<strsafe.h>
#include "Metrics.h"
const int NUMLINES = sizeof(METRICS) / sizeof(METRICS[0]); //寫入文本的行數(shù)
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wndclass; // RegisterClassEx函數(shù)用的WNDCLASSEX結(jié)構(gòu)
TCHAR szClassName[] = TEXT("MyWindow"); // RegisterClassEx函數(shù)注冊(cè)的窗口類的名稱
TCHAR szAppName[] = TEXT("GetSystemMetrics"); // 窗口標(biāo)題
HWND hwnd; // CreateWindowEx函數(shù)創(chuàng)建的窗口的句柄
MSG msg; // 消息循環(huán)所用的消息結(jié)構(gòu)體
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // 窗口背景使用標(biāo)準(zhǔn)系統(tǒng)顏色
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szClassName;
wndclass.hIconSm = NULL;
::RegisterClassEx(&wndclass);
hwnd = ::CreateWindowEx(0, szClassName, szAppName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
::ShowWindow(hwnd, nCmdShow);
::UpdateWindow(hwnd);
while (::GetMessage(&msg, NULL, 0, 0) != 0)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TCHAR szBuf[10];
int y;
if (uMsg == WM_PAINT)
{
hdc = ::BeginPaint(hwnd, &ps);
for (int i = 0; i < NUMLINES; i++)
{
y = 18 * i;
//設(shè)置背景模式是透明的,文本顏色為藍(lán)色
int preSetBkMode = ::SetBkMode(hdc, TRANSPARENT);
COLORREF preSetTextColor = SetTextColor(hdc, RGB(0, 0, 255));
::TextOut(hdc, 0, y, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
//恢復(fù)預(yù)設(shè)
SetBkMode(hdc, preSetBkMode);
SetTextColor(hdc, preSetTextColor);
::TextOut(hdc, 240, y, METRICS[i].m_pDesc, _tcslen(METRICS[i].m_pDesc));
::TextOut(hdc, 760, y, szBuf, wsprintf(szBuf, TEXT("%d"), ::GetSystemMetrics(METRICS[i].m_nIndex)));
::SetTextCharacterExtra(hdc, 0);
}
::EndPaint(hwnd, &ps);
return 0;
}
else if (uMsg == WM_DESTROY)
{
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
可以看到背景模式是透明的,文本顏色為藍(lán)色。
顯示DC的默認(rèn)文本顏色TextColor為黑色,默認(rèn)背景顏色BkColor為白色,默認(rèn)背景模式BkMode為不透明。
程序可以通過調(diào)用GetTextColor函數(shù)獲取DC的當(dāng)前文本顏色。
可以通過調(diào)用GetBkColor函數(shù)獲取DC的當(dāng)前背景顏色。
可以通過調(diào)用GetBkMode函數(shù)獲取DC的當(dāng)前背景模式。
獲取字符串的寬度和高度
GetCharWidth32函數(shù)可以獲取指定DC當(dāng)前字體中指定范圍內(nèi)的連續(xù)字符的寬度︰
BOOL GetCharWidth32
(
_In_ HDC hdc,//設(shè)備環(huán)境句柄
_In_ UINT iFirstChar,//連續(xù)字符中的第一個(gè)字符
_In_ UINT iLastChar,//連續(xù)字符中的最后一個(gè)字符,不得位于指定的第一個(gè)字符之前
_out_ LPINT lpBuffer //接收每個(gè)字符寬度的INT數(shù)組,字符寬度是邏輯單位
);
可以把iFirstCha和iLastChar參數(shù)指定為相同的值,只獲取一個(gè)字符的寬度。
GetTextExtentPoint32函數(shù)用于獲取指定DC中一個(gè)字符串的寬度和高度值︰
BOOL GetTextExtentPoint32
(
_In_ HDC hdc,//設(shè)備環(huán)境句柄
_In_ LPCTSTR lpString,//字符串指針,不要求以零結(jié)尾,因?yàn)閰?shù)c可以指定字符串長(zhǎng)度
_ln_ intc,//字符串長(zhǎng)度,可以使用_tcslen
_Out_LPSIZE lpSize //在這個(gè)SIZE結(jié)構(gòu)中返回字符串的寬度和高度,邏輯單位
);
lpSize是一個(gè)指向SIZE結(jié)構(gòu)的指針,在這個(gè)SIZE結(jié)構(gòu)中返回字符串的寬度和高度。SIZE結(jié)構(gòu)在windef.h頭文件中定義如下∶
typedef struct tagSIZE
{
LONG cx;
LONG cy;
}SIZE,*PSIZE,*LPSIZE;
前面說過∶“WM_CREATE消息是窗口過程較早收到的消息之一,程序通常會(huì)在這里做一些初始化的工作”。對(duì)于SystemMetrics程序,我們可以在WM_CREATE消息中獲取字符串高度,用于在TextOut函數(shù)中指定y坐標(biāo)值︰
#include <Windows.h>
#include <tchar.h>
#include<strsafe.h>
#include "Metrics.h"
const int NUMLINES = sizeof(METRICS) / sizeof(METRICS[0]); //寫入文本的行數(shù)
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wndclass; // RegisterClassEx函數(shù)用的WNDCLASSEX結(jié)構(gòu)
TCHAR szClassName[] = TEXT("MyWindow"); // RegisterClassEx函數(shù)注冊(cè)的窗口類的名稱
TCHAR szAppName[] = TEXT("GetSystemMetrics"); // 窗口標(biāo)題
HWND hwnd; // CreateWindowEx函數(shù)創(chuàng)建的窗口的句柄
MSG msg; // 消息循環(huán)所用的消息結(jié)構(gòu)體
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); // 窗口背景使用標(biāo)準(zhǔn)系統(tǒng)顏色
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szClassName;
wndclass.hIconSm = NULL;
::RegisterClassEx(&wndclass);
hwnd = ::CreateWindowEx(0, szClassName, szAppName, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
::ShowWindow(hwnd, nCmdShow);
::UpdateWindow(hwnd);
while (::GetMessage(&msg, NULL, 0, 0) != 0)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static SIZE size = { 0 };
TCHAR szBuf[10];
int y;
if (uMsg == WM_CREATE)
{
//查字符串高度和寬度
hdc = GetDC(hwnd);
GetTextExtentPoint32(hdc,METRICS[0].m_pLabel,_tcslen(METRICS[0].m_pLabel),&size);
ReleaseDC(hwnd,hdc);
return 0;
}
else if (uMsg == WM_PAINT)
{
hdc = ::BeginPaint(hwnd, &ps);
for (int i = 0; i < NUMLINES; i++)
{
// y = 18 * i; //這里計(jì)算行間距就不用寫死了
y = size.cy * i; //計(jì)算行間距
::TextOut(hdc, 0, y, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
::TextOut(hdc, 240, y, METRICS[i].m_pDesc, _tcslen(METRICS[i].m_pDesc));
::TextOut(hdc, 760, y, szBuf, wsprintf(szBuf, TEXT("%d"), ::GetSystemMetrics(METRICS[i].m_nIndex)));
::SetTextCharacterExtra(hdc, 0);
}
::EndPaint(hwnd, &ps);
return 0;
}
else if (uMsg == WM_DESTROY)
{
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
GetTextExtentPoint32函數(shù)適用于字符串中不包含制表符的情況,如果字符串中包含制表符,則應(yīng)該調(diào)用GetTabbedTextExtent函數(shù)︰
DWORD GetTabbedTextExtent(
_In_ HDC hDC,//設(shè)備環(huán)境句柄
_In_ LPCTSTR lpString,//字符串指針,不要求以需結(jié)尾,因?yàn)閚Count指定字符串長(zhǎng)度
_In_ int nCount, //字符串長(zhǎng)度,可以使用_tcslen
_In_ int nTabPositions,//lpnTabStopPositions數(shù)組中元素的個(gè)數(shù)
_In_opt_ const LPINT lpnTabStopPositions //指向包含制表符位置的數(shù)組
);
-
如果將nTabPositions參數(shù)設(shè)置為0,并將lpnTabStopPositions參數(shù)設(shè)置為NULL,制表符會(huì)自動(dòng)按平均字符寬度的8倍來擴(kuò)展
-
如果將nTabPositions參數(shù)設(shè)置為1,則所有制表符按lpnTabStopPositions參數(shù)指向的數(shù)組中的第一個(gè)數(shù)組元素指定的距離來分隔。
-
如果函數(shù)執(zhí)行成功,則返回值是字符串的寬度和高度(邏輯單位),高度值在高位字中,寬度值在低位字中;如果函數(shù)執(zhí)行失敗,則返回值為0.
HIWORD宏可以得到一個(gè)32位數(shù)的高16位;
LOWORD宏可以得到一個(gè)32位數(shù)的低16位;
HIBYTE宏可以得到一個(gè)16位數(shù)的高字節(jié);
LOBYTE宏可以得到一個(gè)16位數(shù)的低字節(jié)。文章來源:http://www.zghlxwxcb.cn/news/detail-833736.html
類似的還有,MAKELONG宏可以將兩個(gè)16位的數(shù)合成為一個(gè)32位的LONG型;MAKEWORD宏可以將兩個(gè)8位的數(shù)合成為一個(gè)16位的WORD型,等等。這些宏在minwindef.h頭文件中定義如下︰文章來源地址http://www.zghlxwxcb.cn/news/detail-833736.html
#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
#define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
#define LOBYTE(w) ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w) ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))
TabPositions參數(shù)設(shè)置為1,則所有制表符按lpnTabStopPositions參數(shù)指向的數(shù)組中的第一個(gè)數(shù)組元素指定的距離來分隔。
- 如果函數(shù)執(zhí)行成功,則返回值是字符串的寬度和高度(邏輯單位),高度值在高位字中,寬度值在低位字中;如果函數(shù)執(zhí)行失敗,則返回值為0.
HIWORD宏可以得到一個(gè)32位數(shù)的高16位;
LOWORD宏可以得到一個(gè)32位數(shù)的低16位;
HIBYTE宏可以得到一個(gè)16位數(shù)的高字節(jié);
LOBYTE宏可以得到一個(gè)16位數(shù)的低字節(jié)。
類似的還有,MAKELONG宏可以將兩個(gè)16位的數(shù)合成為一個(gè)32位的LONG型;MAKEWORD宏可以將兩個(gè)8位的數(shù)合成為一個(gè)16位的WORD型,等等。這些宏在minwindef.h頭文件中定義如下︰
#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
#define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
#define LOBYTE(w) ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w) ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))
到了這里,關(guān)于37_MFC自繪UI你離不開GDI繪圖的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!