跟我一起玩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修改画刷颜色,然后再画矩形。我们可以看看上面代码的最终效果。
下面,我们做一个人类历史上最简单的画图程序。
我们为程序提供几种可选的线条风格,通过菜单来选择,如实线,虚线等,鼠标按下左键后开始,鼠标左键弹起就完成一条直线的绘制。为了简化,我们把相应菜单的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关键字来声明变量,这样这些变量的生命周期就与整个应用程序相同了。
运行程序后,在菜单中选择一种线型,然后在窗口上画线,效果如图所示。

跟我一起玩Win32开发(10):绘图(C)的更多相关文章
- 跟我一起玩Win32开发(转自CSDN-东邪独孤)
跟我一起玩Win32开发(1):关于C++的几个要点 跟我一起玩Win32开发(2):完整的开发流程 跟我一起玩Win32开发(3):窗口的重绘 跟我一起玩Win32开发(4):创建菜单 跟我一起玩W ...
- 跟我一起玩Win32开发(17):启动和结束进程
这里我再次说明一下,我不知道为什么,现在的人那么喜欢走极端,估计是价值观都“升级”了的缘故吧. 我撰写这一系列Win32相关的文章,并不是叫大家一定要用Win32去开发项目,仅仅是给大家了解一下,Wi ...
- 跟我一起玩Win32开发(18):使用对话框的两个技巧
相信大家知道对话框怎么用了,就是先用“资源编辑器”设计一个对话框,然后在代码中加载处理.今天,我向大家分享两个使用对话框的技巧,还是比较实用的.不用担心,先喝杯茶,很简单的,一点也不复杂,总之,看俺写 ...
- 跟我一起玩Win32开发(9):绘图(B)
我们今天继续涂鸦,实践证明,涂鸦是人生一大乐趣. 首先,我们写一个程序骨架子,以便做实验. #include <Windows.h> LRESULT CALLBACK MainWinPro ...
- 跟我一起玩Win32开发(8):绘图(A)
从本篇开始,我就不吹牛皮,那就吹吹兔皮吧.说说与绘图有关的东东. 要进行绘制,首先要得到一个DC,啥是DC呢?按字面翻译叫设备上下文,也可以翻译为设备描述表,它主要指API为我们封装了一些与显示设备相 ...
- 跟我一起玩Win32开发(12):使用控件——单选按钮
今天,咱们还是接着玩“控件斗地主”,这是我原创的超级游戏,有益身心健康,玩一朝,十年少. 哦,对,脑细胞极速运动了一下,想起了一个问题,这个破问题虽然网上有很多种解决方案,但是,并没有让所有人都解决问 ...
- 跟我一起玩Win32开发(23):渐变颜色填充
GradientFill函数可以对特定的矩形区域或者三角形区域进行渐变颜色的填充.我们先来看看GradientFill函数到底长得什么样子,帅不帅. BOOL GradientFill( _In_ ...
- 跟我一起玩Win32开发(19):浏览和打开文件
在应用程序中,我们很经常要实现的功能,是Open文件或保存文件对话框,让用户来选择一个或N个文件.本文我将介绍两种思路,第一种方法较为复杂,第二种方法较为简单. 方法一:老规矩 这是一种传统方法,使用 ...
- 跟我一起玩Win32开发(2):完整的开发流程
上一篇中我给各位说了一般人认为C++中较为难的东西——指针.其实对于C++,难点当然不局限在指针这玩意儿上,还有一些有趣的概念,如模板类.虚基类.纯虚函数等,这些都是概念性的东西,几乎每一本C++书上 ...
随机推荐
- [转载]JSONP跨域的原理解析
JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略).这一策略对于Java ...
- AVL树,红黑树,B-B+树,Trie树原理和应用
前言:本文章来源于我在知乎上回答的一个问题 AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中? 看完后您可能会了解到这些数据结构大致的原理及为什么用在这些场景,文章并不涉及具体操作 ...
- [Elasticsearch] 部分匹配 (四) - 索引期间优化ngrams及索引期间的即时搜索
本章翻译自Elasticsearch官方指南的Partial Matching一章. 索引期间的优化(Index-time Optimizations) 眼下我们讨论的全部方案都是在查询期间的.它们不 ...
- oracle的shared、dedicated模式解析
主要參考文档:http://www.itpub.net/thread-1714191-1-1.html Oracleh有两种server模式shared mode和dedicated mode. De ...
- sanic官方文档解析之ssl,debug mode模式和test(测试)
1,ssl 示例: 可选择的SSLContent from sanic import Sanic import ssl context = ssl.create_default_context(pur ...
- jetty java文件无法删除 java文件占用 delete无效 运行时锁定静态资源的解决方法
前几天jetty下发现java无法删除文件,文件操作后一直被jvm占用,无奈换了tomcat问题消失. 今天又想起来,尝试网上的解决方法,经本人试验,直接修改配置文件有时不能生效,具体原因不清楚,建议 ...
- JAVA变量存储
1.java变量存储域 java变量的存储区域主要放在以下几个地方: (1)寄存器:可以说是最快的存储区,在C/C++中可以声明寄存器变量,但是在java中不能声明寄存器变量,只是编译器在编译时确定. ...
- leetcode 684. Redundant Connection
We are given a "tree" in the form of a 2D-array, with distinct values for each node. In th ...
- HDU1532 Drainage Ditches —— 最大流(sap算法)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1532 Drainage Ditches Time Limit: 2000/1000 MS (Java/ ...
- SQL Server 数据库备份策略,第一周运行失败的原因
一般生产库,采用 每10分钟备份Log,每天备份Diff,每周备份Full的策略. 同时存在异地备份.异地备份可使用SQL Server本身的cmdshell存储过程,调用系统命令. 在为新数据库,建 ...