跟小弟我一起玩Win32开发(10):绘图(C)

跟我一起玩Win32开发(10):绘图(C)

今天我们来欣赏一下用于填充图形的函数,当然我不会逐个去介绍,因为我们参考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修改画刷颜色,然后再画矩形。我们可以看看上面代码的最终效果。

跟小弟我一起玩Win32开发(10):绘图(C)


 

下面,我们做一个人类历史上最简单的画图程序。

我们为程序提供几种可选的线条风格,通过菜单来选择,如实线,虚线等,鼠标按下左键后开始,鼠标左键弹起就完成一条直线的绘制。为了简化,我们把相应菜单的ID设置的值与CreatePen中的线型的宏的值一致。

跟小弟我一起玩Win32开发(10):绘图(C)

这样一来,选择了哪个菜单就直接用这个菜单的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关键字来声明变量,这样这些变量的生命周期就与整个应用程序相同了。

运行程序后,在菜单中选择一种线型,然后在窗口上画线,效果如图所示。

跟小弟我一起玩Win32开发(10):绘图(C)

 

跟小弟我一起玩Win32开发(10):绘图(C)

 

1楼ChoiSooYoung4天前 13:20
支持楼主一下 希望楼主继续写