上一篇我們展示了如何使用Windows SDK創建基本控件,本篇來討論如何輸出文本字符。
在使用Win32編程時,我們常常要輸出文本到窗口上,Windows所有的文本字符或者圖形輸出都是通過圖形設備接口(GDI)進行的,Windows的三大核心組件之一的GDI32.dll封裝了所有的文本和圖像輸出。
- GDI基本知識
Windows下要繪圖和輸出文本,都是通過GDI(Graphics Device Interface,圖形設備接口)完成的,GDI是windows在繪制圖文時的設備上下文環境,包括畫筆、畫刷、字體、位圖等多種與繪制有關的對象。設備環境(DC)在繪制中起至關重要的作用。幾乎所有的繪制(包括圖形和文本)都與設備環境相關,注意“環境”的意義,就跟我們在畫布上繪畫和寫字一樣,繪制時的畫布是哪個,用的什么筆,什么顏色,填充整個畫布時用的什么刷子等等,這就是我們的繪制時的環境,而Windows繪圖的DC設備上下文就是一樣的道理。設備環境句柄(HDC)就是用來描述DC的句柄,可以說,只要有了這個句柄,就具備了在窗口上輸出圖形和文本的條件。你獲得了窗口客戶區的HDC,就可以在窗口客戶區上畫;你獲得了窗口的非客戶區HDC,就可以在它上面畫;你獲得了桌面HDC,就可以直接在桌面上畫……
獲取設備環境句柄的方法有兩種:一是處理WM_PAINT消息時,通過BeginPaint函數返回。另外一種就是通過GetDC、GetWindowDC的API函數獲取。
- 通過WM_PAINT消息獲取DC
Windows在檢測到需要重新繪制或者刷新窗口時,會主動要求處理WM_PAINT消息。比如在如下情況下就會主動求處理:
- 用戶移動一個窗口,導致原來被蓋住的部分窗口顯示出來。
- 用戶調整窗口的大小,並且窗口風格類型設置為CS_HREDRAW和CS_VREDRAW。
- 程序調用ScrollWindow或者ScrollDC函數滾動客戶區。
- 程序調用InvalidateRect或者InvalidateRgn函數,該函數顯示生產一條WM_PAINT消息。
我們可以在該消息中完成圖文繪制,該消息的處理具有特定的格式,必須在實際繪制前調用BeginPaint,在繪制完成后調用EndPaint函數,也就是說我們需要把所有繪制的功能都放到這兩個函數之間,並且HDC也只能在這之間使用,不能保存起來在其它地方使用。使用WM_PAINT有一個好處,就是windows會自己計算哪些區域需要更新,也就是說只有真正變化的地方才會更新,這樣更新的代價會降低到最小。
- 通過API函數獲取HDC
我們還可以通過GetDC、GetWindowDC函數來獲取HDC,但是要注意,通過這個來獲取的HDC,可以保存起來在其它時候使用,但是要記住一旦窗口有更新,必須想辦法重新繪制,否則就會消失了。最后在使用完畢后需要調用ReleaseDC來釋放,否則會造成資源泄露。
- 創建特定字體
我們平時最常見的文本輸出是不需要自己創建字體的,因為常見的對象都有系統預定義好的。如果想輸出點特殊(非系統預定義的)字體,就需要我們創建並自動選入設備環境。創建字體主要有CreateFont和CreateFontIndirect,這兩個函數的參數都很多,基本一樣,具體用法看后面的實例。
- 實現文本繪制
有了上面的基礎,我們就可以通過Windows的API來完成文本輸出了,常用的文本輸出函數有TextOut、DrawText、DrawTextExt、ExtTextOut等,這些函數基本都有相似的參數,比如hdc,坐標位置,字符串。下面TextOut、DrawText、ExtTextOut為例來說明如何在Windows窗口中如何輸出文本,其它請查看MSDN的用法。
#include <windows.h>
#include <tchar.h>
static TCHAR szAppName[] = TEXT("Textout");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox (NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hWnd = CreateWindow(szAppName, // window class name
szAppName, // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
400, // initial x size
300, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
return 0;
case WM_PAINT:
{
RECT rect = {10, 30, 100, 50};
TCHAR str[] = TEXT("English and 中文");
hDC = BeginPaint(hWnd, &ps);
TextOut(hDC, 10, 10, str, _tcslen(str));
SetTextColor(hDC, RGB(255,0,0));
DrawText(hDC, str, -1, &rect, DT_LEFT|DT_VCENTER);
SetTextColor(hDC, RGB(0,255,0));
INT dx[] = {8,8,8,8,16,8,8,8,16,8,8,8,10};
ExtTextOut(hDC, 10, 50, 0, &rect, str, _tcslen(str), dx);
SetTextColor(hDC, RGB(0,0,255));
rect.right = 110;
rect.top = 70;
rect.bottom = 82;
ExtTextOut(hDC, 10, rect.top, ETO_CLIPPED, &rect, str, _tcslen(str), dx);
HFONT hFont = CreateFont(96, // nHeight, 所創建字體的字符高度
0, // nWidth, 字體的字符平均寬度
200, // nEscapement, 字符輸出方向與水平向右的方向所成角度,以0.1度為單位
0, // nOrientation, 字符與基線的角度,以0.1度為單位
FW_BOLD, // nWeight, 字符顏色的深淺度
TRUE, // bItalic, 斜體屬性標志(FALSE:正常字體,TRUE:斜體)
FALSE, // bUnderline, 下划線屬性標志(FALSE:無下划線,TRUE:有下划線)
FALSE, // cStrikeOut, 刪除線屬性標志(FALSE:無刪除線,TRUE:有刪除線)
ANSI_CHARSET, // nCharSet, 字符集標識0:ANSI字符集,1:系統缺省字符集
OUT_DEFAULT_PRECIS, // nOutPrecision, 輸出精度
CLIP_DEFAULT_PRECIS, // nClipPrecision, 剪切精度
DEFAULT_QUALITY, // nQuality, 輸出品質
DEFAULT_PITCH|FF_SWISS, // nPitchAndFamily, 字符間距
TEXT("Arial")); // lpszFacename, 現有系統TrueType字體名稱
HFONT hOldFont = (HFONT)SelectObject(hDC, hFont);
SetBkMode(hDC, TRANSPARENT);
SetTextColor(hDC, RGB(0x00, 0xFF, 0xFF));
TextOut(hDC, 0, 150, TEXT("創建Font"), 6);
DeleteObject(hFont);
EndPaint(hWnd, &ps);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0 ;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
程序運行,點擊鼠標左鍵后效果如下:
程序中的DrawText、ExtTextOut能設置文本輸出的矩形范圍,超出部分是看不見的,從運行結果我們也可以看出有兩行顯示不全,就是由於設置的顯示范圍小的緣故。
另外ExtTextOut函數還可以設置字符的間距,運行結果的第三行就是這種自己設置間距不一樣的結果。
本程序還用CreateFont函數創建了一個斜體、右上排列的文本串。通過上例,我們把常用的文本輸出作為實例展示給大家,只要好好對照實例代碼,在結合MSDN的說明,再加上本系列的第一篇的Windows編程基本框架,一定可以掌握好Windows編程的基本文本輸出。
關注微信公眾平台:程序員互動聯盟(coder_online),你可以第一時間獲取原創技術文章,和(java/C/C++/Android/Windows/Linux)技術大牛做朋友,在線交流編程經驗,獲取編程基礎知識,解決編程問題。程序員互動聯盟,開發人員自己的家。
轉載請注明出處,謝謝合作!



