今天我們來欣賞一下用于填充圖形的函數,當然我不會逐個去介紹,因為我們參考MSDN直接套參數就可以了。
SetDCBrushColor函數有必要扯一下,它的聲明如下:
~~~
COLORREF SetDCBrushColor(
__in HDC hdc,
__in COLORREF crColor
);
~~~
第二個參數,通過RGB宏產生COLORREF傳進去就可以了,比如這樣:
~~~
SetDCBrushColor(ps.hdc,RGB(211,254,41));
~~~
但是,如果只是調用這個函數,你會發現在繪圖的時候,畫刷的顏色還是沒有變化,因為我們還沒有將HBRUSH的默認畫刷DC_BRUSH選到DC中去。所以,在調用SetDCBrushColor之前,要把默認的畫刷先放到設備上下文,默認畫刷可以通過GetStockObject(DC_BRUSH)獲得。
~~~
SelectObject(ps.hdc,GetStockObject(DC_BRUSH));
~~~
接下來我們可以嘗試填充幾個圖形試試,如矩形、橢圓、餅圖等。
~~~
case WM_PAINT:
{
BeginPaint(hwnd,&ps);
SelectObject(ps.hdc,GetStockObject(DC_BRUSH));
SetDCBrushColor(ps.hdc,RGB(0,0,255));
Rectangle(ps.hdc,20,18,68,50);
SetDCBrushColor(ps.hdc,RGB(220,32,70));
Rectangle(ps.hdc,125,100,230,300);
SetDCBrushColor(ps.hdc,RGB(30,235,12));
Ellipse(ps.hdc,270,80,390,223);
SetDCBrushColor(ps.hdc,RGB(35,160,242));
Chord(ps.hdc,185,260,420,480,190,260,405,479);
SetDCBrushColor(ps.hdc,RGB(211,254,41));
Pie(ps.hdc,35,320,300,600,56,470,60,360);
EndPaint(hwnd,&ps);
}
return 0;
~~~
每一次調用SetDCBrushColor都會改變畫刷的顏色,所以,比如你希望繪制藍色的矩形,在調用Rectangle之前就要調用SetDCBrushColor修改畫刷顏色,然后再畫矩形。我們可以看看上面代碼的最終效果。

下面,我們做一個人類歷史上最簡單的畫圖程序。
我們為程序提供幾種可選的線條風格,通過菜單來選擇,如實線,虛線等,鼠標按下左鍵后開始,鼠標左鍵彈起就完成一條直線的繪制。為了簡化,我們把相應菜單的ID設置的值與CreatePen中的線型的宏的值一致。

這樣一來,選擇了哪個菜單就直接用這個菜單的ID來創建畫筆,就省去了許多代碼。
在響應WM_CREATE消息時,創建菜單。
~~~
case WM_CREATE:
{
// 創建菜單
HMENU menubar = CreateMenu();
HMENU menuPop = CreatePopupMenu();
AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_SOLID,L"實線");
AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DASH,L"虛線");
AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DOT,L"點線");
AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DASHDOT,L"點虛線");
AppendMenu(menubar, MF_STRING | MF_POPUP, (UINT_PTR)menuPop, L"選擇線型");
SetMenu(hwnd, menubar);
}
return 0;
~~~
現在我們來想一下,繪制直線的大概思路。
1、鼠標左鍵按下,記錄線條的起點。
2、鼠標左鍵彈起時,記錄線條的終點,并畫出整條線。
3、當窗口發生重繪時,前面畫的所有線條被清除,要希望保留前面畫的線條,就要響應WM_PAINT消息,把所有線條重新畫一次。
4、由于我們會在窗口上畫出多條線,程序需要定義一個結構體用來保存線條的起點、終點和所使用的線型。
5、正因為需要保存多條線的數據,故可以把每一條線的相關數據放到一個vector中。
根據上面的分析,完成程序的代碼如下:
~~~
#include <Windows.h>
#include <WindowsX.h>
#include <vector>
using namespace std;
typedef struct tagData
{
int ptBeginX, ptBeginY;//起點
int ptEndX, ptEndY;//終點
int penStyle;//畫筆的線型
} PAINTDATA;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(
HINSTANCE hThisApp,
HINSTANCE hPrevApp,
LPSTR lpsCmd,
int nShow)
{
WNDCLASS wc = {};
wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
wc.hInstance = hThisApp;
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = L"My";
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
HWND hwnd = CreateWindow(
L"My",
L"應用程序",
WS_OVERLAPPEDWINDOW,
50,
20,
600,
480,
NULL,
NULL,
hThisApp,
NULL);
if(hwnd == NULL)
return -1;
ShowWindow(hwnd, nShow);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static vector<PAINTDATA> datas;
static int penStyle = PS_SOLID;
static PAINTDATA *pCurrentData = NULL;//指向當前PAINTDATA的指針
switch(msg)
{
case WM_CREATE:
{
// 創建菜單
HMENU menubar = CreateMenu();
HMENU menuPop = CreatePopupMenu();
AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_SOLID,L"實線");
AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DASH,L"虛線");
AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DOT,L"點線");
AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DASHDOT,L"點虛線");
AppendMenu(menubar, MF_STRING | MF_POPUP, (UINT_PTR)menuPop, L"選擇線型");
SetMenu(hwnd, menubar);
}
return 0;
case WM_COMMAND:
{
//為選中的菜單加上單選標記
HMENU mnbar = GetMenu(hwnd);
HMENU hmnPop = GetSubMenu(mnbar, 0);
CheckMenuRadioItem(hmnPop, PS_SOLID, PS_DASHDOT, LOWORD(wParam), MF_BYCOMMAND);
//記錄用戶選擇的線型
penStyle = (int)LOWORD(wParam);
}
return 0;
case WM_LBUTTONDOWN:
{
pCurrentData = new PAINTDATA;
//獲取起點
pCurrentData ->penStyle = penStyle;
pCurrentData->ptBeginX = GET_X_LPARAM(lParam);
pCurrentData->ptBeginY = GET_Y_LPARAM(lParam);
}
return 0;
case WM_LBUTTONUP:
{
if(pCurrentData != NULL)
{
//獲取終點
pCurrentData->ptEndX = GET_X_LPARAM(lParam);
pCurrentData->ptEndY = GET_Y_LPARAM(lParam);
//畫出線條
HDC hdc = GetDC(hwnd);
HPEN pen = CreatePen(pCurrentData->penStyle,1,RGB(0,255,0));
HPEN oldpen = (HPEN)SelectObject(hdc,pen);
MoveToEx(hdc,pCurrentData->ptBeginX,pCurrentData->ptBeginY,NULL);
LineTo(hdc,pCurrentData->ptEndX,pCurrentData->ptEndY);
SelectObject(hdc,oldpen);
DeleteObject(pen);
ReleaseDC(hwnd,hdc);
//把當前數據添加到vector中
datas.push_back(*pCurrentData);
}
}
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd,&ps);
//將所有線條重新畫一遍
vector<PAINTDATA>::const_iterator item;
for(item = datas.begin(); item != datas.end(); item++)
{
HPEN pen = CreatePen(item->penStyle, 1, RGB(0,255,0));
SelectObject(ps.hdc, pen);
MoveToEx(ps.hdc, item->ptBeginX, item->ptBeginY, NULL);
LineTo(ps.hdc, item->ptEndX, item->ptEndY);
DeleteObject(pen);
}
EndPaint(hwnd,&ps);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
~~~
結構體PAINTDATA用來保存每一條線的起點坐標、終點坐標、線型。為了避免在跳出WindowProc后所有數據被回收,可以使用static關鍵字來聲明變量,這樣這些變量的生命周期就與整個應用程序相同了。
運行程序后,在菜單中選擇一種線型,然后在窗口上畫線,效果如圖所示。


- 前言
- (1):關于C++的幾個要點
- (2):完整的開發流程
- (3):窗口的重繪
- (4):創建菜單
- (5):具有單選標記的菜單
- (6):創建右鍵菜單
- (7):多邊形窗口
- (8):繪圖(A)
- (9):繪圖(B)
- (10):繪圖(C)
- (11):使用控件——先來耍一下按鈕
- (12):使用控件——單選按鈕
- (13):握手對話框
- (14):用對話框作為主窗口
- (15):ListView控件
- (16):ListView的多個視圖
- (17):啟動和結束進程
- (18):使用對話框的兩個技巧
- (19):瀏覽和打開文件
- (20):瀏覽文件夾
- (21):復制&amp;粘貼&amp;剪貼板操作
- (22):抓取屏幕
- (23):漸變顏色填充
- (24):計時器
- (25):監視剪貼板