今天我们来欣赏一下用于填充图形的函数,当然我不会逐个去介绍,因为我们参考MSDN直接套参数就可以了。

SetDCBrushColor函数有必要扯一下,它的声明如下:

  1. COLORREF SetDCBrushColor(
  2. __in  HDC hdc,
  3. __in  COLORREF crColor
  4. );

第二个参数,通过RGB宏产生COLORREF传进去就可以了,比如这样:

  1. SetDCBrushColor(ps.hdc,RGB(211,254,41));

但是,如果只是调用这个函数,你会发现在绘图的时候,画刷的颜色还是没有变化,因为我们还没有将HBRUSH的默认画刷DC_BRUSH选到DC中去。所以,在调用SetDCBrushColor之前,要把默认的画刷先放到设备上下文,默认画刷可以通过GetStockObject(DC_BRUSH)获得。

  1. SelectObject(ps.hdc,GetStockObject(DC_BRUSH));

接下来我们可以尝试填充几个图形试试,如矩形、椭圆、饼图等。

  1. case WM_PAINT:
  2. {
  3. BeginPaint(hwnd,&ps);
  4. SelectObject(ps.hdc,GetStockObject(DC_BRUSH));
  5. SetDCBrushColor(ps.hdc,RGB(0,0,255));
  6. Rectangle(ps.hdc,20,18,68,50);
  7. SetDCBrushColor(ps.hdc,RGB(220,32,70));
  8. Rectangle(ps.hdc,125,100,230,300);
  9. SetDCBrushColor(ps.hdc,RGB(30,235,12));
  10. Ellipse(ps.hdc,270,80,390,223);
  11. SetDCBrushColor(ps.hdc,RGB(35,160,242));
  12. Chord(ps.hdc,185,260,420,480,190,260,405,479);
  13. SetDCBrushColor(ps.hdc,RGB(211,254,41));
  14. Pie(ps.hdc,35,320,300,600,56,470,60,360);
  15. EndPaint(hwnd,&ps);
  16. }
  17. return 0;

每一次调用SetDCBrushColor都会改变画刷的颜色,所以,比如你希望绘制蓝色的矩形,在调用Rectangle之前就要调用SetDCBrushColor修改画刷颜色,然后再画矩形。我们可以看看上面代码的最终效果。

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

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

这样一来,选择了哪个菜单就直接用这个菜单的ID来创建画笔,就省去了许多代码。

在响应WM_CREATE消息时,创建菜单。

  1. case WM_CREATE:
  2. {
  3. // 创建菜单
  4. HMENU menubar = CreateMenu();
  5. HMENU menuPop = CreatePopupMenu();
  6. AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_SOLID,L"实线");
  7. AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DASH,L"虚线");
  8. AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DOT,L"点线");
  9. AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DASHDOT,L"点虚线");
  10. AppendMenu(menubar, MF_STRING | MF_POPUP, (UINT_PTR)menuPop, L"选择线型");
  11. SetMenu(hwnd, menubar);
  12. }
  13. return 0;

现在我们来想一下,绘制直线的大概思路。

1、鼠标左键按下,记录线条的起点。

2、鼠标左键弹起时,记录线条的终点,并画出整条线。

3、当窗口发生重绘时,前面画的所有线条被清除,要希望保留前面画的线条,就要响应WM_PAINT消息,把所有线条重新画一次。

4、由于我们会在窗口上画出多条线,程序需要定义一个结构体用来保存线条的起点、终点和所使用的线型。

5、正因为需要保存多条线的数据,故可以把每一条线的相关数据放到一个vector中。

根据上面的分析,完成程序的代码如下:

  1. #include <Windows.h>
  2. #include <WindowsX.h>
  3. #include <vector>
  4. using namespace std;
  5. typedef struct tagData
  6. {
  7. int ptBeginX, ptBeginY;//起点
  8. int ptEndX, ptEndY;//终点
  9. int penStyle;//画笔的线型
  10. } PAINTDATA;
  11. LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  12. int WINAPI WinMain(
  13. HINSTANCE hThisApp,
  14. HINSTANCE hPrevApp,
  15. LPSTR lpsCmd,
  16. int nShow)
  17. {
  18. WNDCLASS wc = {};
  19. wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
  20. wc.hInstance = hThisApp;
  21. wc.lpfnWndProc = WindowProc;
  22. wc.lpszClassName = L"My";
  23. wc.style = CS_HREDRAW | CS_VREDRAW;
  24. RegisterClass(&wc);
  25. HWND hwnd = CreateWindow(
  26. L"My",
  27. L"应用程序",
  28. WS_OVERLAPPEDWINDOW,
  29. 50,
  30. 20,
  31. 600,
  32. 480,
  33. NULL,
  34. NULL,
  35. hThisApp,
  36. NULL);
  37. if(hwnd == NULL)
  38. return -1;
  39. ShowWindow(hwnd, nShow);
  40. UpdateWindow(hwnd);
  41. MSG msg;
  42. while(GetMessage(&msg,NULL,0,0))
  43. {
  44. TranslateMessage(&msg);
  45. DispatchMessage(&msg);
  46. }
  47. return 0;
  48. }
  49. LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  50. {
  51. static vector<PAINTDATA> datas;
  52. static int penStyle = PS_SOLID;
  53. static PAINTDATA *pCurrentData = NULL;//指向当前PAINTDATA的指针
  54. switch(msg)
  55. {
  56. case WM_CREATE:
  57. {
  58. // 创建菜单
  59. HMENU menubar = CreateMenu();
  60. HMENU menuPop = CreatePopupMenu();
  61. AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_SOLID,L"实线");
  62. AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DASH,L"虚线");
  63. AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DOT,L"点线");
  64. AppendMenu(menuPop,MF_STRING,(UINT_PTR)PS_DASHDOT,L"点虚线");
  65. AppendMenu(menubar, MF_STRING | MF_POPUP, (UINT_PTR)menuPop, L"选择线型");
  66. SetMenu(hwnd, menubar);
  67. }
  68. return 0;
  69. case WM_COMMAND:
  70. {
  71. //为选中的菜单加上单选标记
  72. HMENU mnbar = GetMenu(hwnd);
  73. HMENU hmnPop = GetSubMenu(mnbar, 0);
  74. CheckMenuRadioItem(hmnPop, PS_SOLID, PS_DASHDOT, LOWORD(wParam), MF_BYCOMMAND);
  75. //记录用户选择的线型
  76. penStyle = (int)LOWORD(wParam);
  77. }
  78. return 0;
  79. case WM_LBUTTONDOWN:
  80. {
  81. pCurrentData = new PAINTDATA;
  82. //获取起点
  83. pCurrentData ->penStyle = penStyle;
  84. pCurrentData->ptBeginX = GET_X_LPARAM(lParam);
  85. pCurrentData->ptBeginY = GET_Y_LPARAM(lParam);
  86. }
  87. return 0;
  88. case WM_LBUTTONUP:
  89. {
  90. if(pCurrentData != NULL)
  91. {
  92. //获取终点
  93. pCurrentData->ptEndX = GET_X_LPARAM(lParam);
  94. pCurrentData->ptEndY = GET_Y_LPARAM(lParam);
  95. //画出线条
  96. HDC hdc = GetDC(hwnd);
  97. HPEN pen = CreatePen(pCurrentData->penStyle,1,RGB(0,255,0));
  98. HPEN oldpen = (HPEN)SelectObject(hdc,pen);
  99. MoveToEx(hdc,pCurrentData->ptBeginX,pCurrentData->ptBeginY,NULL);
  100. LineTo(hdc,pCurrentData->ptEndX,pCurrentData->ptEndY);
  101. SelectObject(hdc,oldpen);
  102. DeleteObject(pen);
  103. ReleaseDC(hwnd,hdc);
  104. //把当前数据添加到vector中
  105. datas.push_back(*pCurrentData);
  106. }
  107. }
  108. return 0;
  109. case WM_PAINT:
  110. {
  111. PAINTSTRUCT ps;
  112. BeginPaint(hwnd,&ps);
  113. //将所有线条重新画一遍
  114. vector<PAINTDATA>::const_iterator item;
  115. for(item = datas.begin(); item != datas.end(); item++)
  116. {
  117. HPEN pen = CreatePen(item->penStyle, 1, RGB(0,255,0));
  118. SelectObject(ps.hdc, pen);
  119. MoveToEx(ps.hdc, item->ptBeginX, item->ptBeginY, NULL);
  120. LineTo(ps.hdc, item->ptEndX, item->ptEndY);
  121. DeleteObject(pen);
  122. }
  123. EndPaint(hwnd,&ps);
  124. }
  125. return 0;
  126. case WM_DESTROY:
  127. PostQuitMessage(0);
  128. return 0;
  129. default:
  130. return DefWindowProc(hwnd,msg,wParam,lParam);
  131. }
  132. return 0;
  133. }

结构体PAINTDATA用来保存每一条线的起点坐标、终点坐标、线型。为了避免在跳出WindowProc后所有数据被回收,可以使用static关键字来声明变量,这样这些变量的生命周期就与整个应用程序相同了。

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

跟我一起玩Win32开发(10):绘图(C)的更多相关文章

  1. 跟我一起玩Win32开发(转自CSDN-东邪独孤)

    跟我一起玩Win32开发(1):关于C++的几个要点 跟我一起玩Win32开发(2):完整的开发流程 跟我一起玩Win32开发(3):窗口的重绘 跟我一起玩Win32开发(4):创建菜单 跟我一起玩W ...

  2. 跟我一起玩Win32开发(17):启动和结束进程

    这里我再次说明一下,我不知道为什么,现在的人那么喜欢走极端,估计是价值观都“升级”了的缘故吧. 我撰写这一系列Win32相关的文章,并不是叫大家一定要用Win32去开发项目,仅仅是给大家了解一下,Wi ...

  3. 跟我一起玩Win32开发(18):使用对话框的两个技巧

    相信大家知道对话框怎么用了,就是先用“资源编辑器”设计一个对话框,然后在代码中加载处理.今天,我向大家分享两个使用对话框的技巧,还是比较实用的.不用担心,先喝杯茶,很简单的,一点也不复杂,总之,看俺写 ...

  4. 跟我一起玩Win32开发(9):绘图(B)

    我们今天继续涂鸦,实践证明,涂鸦是人生一大乐趣. 首先,我们写一个程序骨架子,以便做实验. #include <Windows.h> LRESULT CALLBACK MainWinPro ...

  5. 跟我一起玩Win32开发(8):绘图(A)

    从本篇开始,我就不吹牛皮,那就吹吹兔皮吧.说说与绘图有关的东东. 要进行绘制,首先要得到一个DC,啥是DC呢?按字面翻译叫设备上下文,也可以翻译为设备描述表,它主要指API为我们封装了一些与显示设备相 ...

  6. 跟我一起玩Win32开发(12):使用控件——单选按钮

    今天,咱们还是接着玩“控件斗地主”,这是我原创的超级游戏,有益身心健康,玩一朝,十年少. 哦,对,脑细胞极速运动了一下,想起了一个问题,这个破问题虽然网上有很多种解决方案,但是,并没有让所有人都解决问 ...

  7. 跟我一起玩Win32开发(23):渐变颜色填充

    GradientFill函数可以对特定的矩形区域或者三角形区域进行渐变颜色的填充.我们先来看看GradientFill函数到底长得什么样子,帅不帅. BOOL GradientFill( _In_   ...

  8. 跟我一起玩Win32开发(19):浏览和打开文件

    在应用程序中,我们很经常要实现的功能,是Open文件或保存文件对话框,让用户来选择一个或N个文件.本文我将介绍两种思路,第一种方法较为复杂,第二种方法较为简单. 方法一:老规矩 这是一种传统方法,使用 ...

  9. 跟我一起玩Win32开发(2):完整的开发流程

    上一篇中我给各位说了一般人认为C++中较为难的东西——指针.其实对于C++,难点当然不局限在指针这玩意儿上,还有一些有趣的概念,如模板类.虚基类.纯虚函数等,这些都是概念性的东西,几乎每一本C++书上 ...

随机推荐

  1. Redis+EJB实现缓存(一)

        上篇博客大概的对Redis做了一个主要的了解.由于刚刚接触自己也不太明确.所以上篇博客写的乱七八糟的.这篇由于项目须要,学习了一下Redis和EJB集成. 如今脑子相对照较清晰了一些. 实现思 ...

  2. 用 querySelectorAll 来查询 DOM 节点

    用 querySelectorAll 来查询 DOM 节点 Document.querySelectorAll - Web API 接口 | MDN https://developer.mozilla ...

  3. 端口扫描 开启 防火墙 iptables SELinux

    Linux 如何打开端口 - lclc - 博客园 https://www.cnblogs.com/lcword/p/5869522.html linux如何查看端口相关信息_百度经验 https:/ ...

  4. leetCode 116.Populating Next Right Pointers in Each Node (为节点填充右指针) 解题思路和方法

    Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...

  5. SpringMvc參数的接受以及serializeArray的使用方法

    需求:从页面提交一个table中的数据到后台,通经常使用于批量改动 把全部的数据到放到 input属性中,设置name定义成为对象的相关属性,使用Jquery的serializeArray这种方法封装 ...

  6. java.lang.IllegalStateException: No instances available for localhost

    在SpringCloud的项目中,我们使用了自动配置的OAuth2RestTemplate,RestTemplate,但是在使用这些restTemplate的时候,url必须是服务的名称,如果要调用真 ...

  7. 目录操作(PHP)

    1.创建目录(文件夹)mkdir("./test");2.删除目录(文件夹)只能删除空的文件夹rmdir("./test");3.移动目录(文件夹)rename ...

  8. 书写优雅的shell脚本(四) - kill命令的合理使用

    Linux中的kill命令用来终止指定的进程(terminate a process)的运行,是Linux下进程管理的常用命令.通常,终止一个前台进程可以使用Ctrl+C键,但是,对于一个后台进程就须 ...

  9. BZOJ_1406_[AHOI2007]密码箱_枚举+数学

    BZOJ_1406_[AHOI2007]密码箱_枚举+数学 Description 在一次偶然的情况下,小可可得到了一个密码箱,听说里面藏着一份古代流传下来的藏宝图,只要能破解密码就能打开箱子,而箱子 ...

  10. Cocos2dx+lua合适还是Cocos2dx+js合适?

    问题: 开发cocos2dx手游Cocos2dx+lua合适还是Cocos2dx+js合适 百牛信息技术bainiu.ltd整理发布于博客园 回答: 作者:廖宇雷链接:https://www.zhih ...