用户通过控件与应用程序交互,在吹牛之前,先介绍一个工具,这是官方的工具,使用它,你可以预览常用控件的外观、样式,以及对控进行操作时接收和发送哪些消息。下载地址如下:

http://www.microsoft.com/en-us/download/details.aspx?id=4635

我们可以把控件当成特殊的一类窗口,所以,创建控件与创建窗口一样,使用CreateWindow或CreateWindowEx函数,不过,在窗口样式上面记得用上以下两位帅哥:

a、WS_CHILD:控件是放在我们的窗口上的,自然要作为窗口的子窗口,WS_CHILDWINDOW也一样,为了节约几个字母,用WS_CHILD吧。

b、WS_VISIBLE:既然要使用控件,自然要让别人看得见。要想别人称赞你老婆漂亮,当然要先让别人见一见你老婆了,哈哈,不要想歪了。

理论的东西,怎么说都是抽象的,咱们还是“所见即所得”吧。那么,到底在啥时候创建控件合适一点呢?一种方法是在WinMain方法中创建,把CreateWindow函数的hWndParent设置为窗口的句柄。

俺这里用第二种方法,我们知道,在窗口创建后,显示之前,即CreateWindow函数返回之前,我们会收到WM_CREATE消息,我们响应它的号召,艰苦奋斗创建一个按钮。

  1. case WM_CREATE:
  2. {
  3. //创建按钮
  4. HWND hButton = CreateWindow(L"Button", L"有种就来点击我!", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
  5. 35, 45, 160, 65, hwnd, NULL, hg_app, NULL);
  6. }
  7. return 0;

hg_app是我定义的一个全局变量,当前应用程序的句柄(HINSTANCE类型)。

然后,运行一下,先看看效果再说吧。

接下来,新的问题来了,按钮我是创建了,但怎么响应用户点击呢?其实,这按钮与菜单项一样,单击用户与她“亲密”接触后,我们的WindowProc会收到WM_COMMAND消息,和菜单一样。

wParam的低字节位表示ID号,高字节位表示控件通知,比如用户单击了按钮,通知码为BN_CLICKED,这样我们就可以了解到用户具体对按钮干了什么。

lParam中保存了控件的句柄。

问题是,怎么设置控件的ID?我们看看CreateWindow的文档介绍。

  1. HWND WINAPI CreateWindow(
  2. _In_opt_  LPCTSTR lpClassName,
  3. _In_opt_  LPCTSTR lpWindowName,
  4. _In_      DWORD dwStyle,
  5. _In_      int x,
  6. _In_      int y,
  7. _In_      int nWidth,
  8. _In_      int nHeight,
  9. _In_opt_  HWND hWndParent,
  10. _In_opt_  HMENU hMenu,
  11. _In_opt_  HINSTANCE hInstance,
  12. _In_opt_  LPVOID lpParam
  13. );

hMenu [in, optional] 
Type: HMENU
A handle to a menu, or specifies a child-window identifier depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used. For a child window, hMenu specifies the child-window identifier, an integer value used by a dialog box control to notify its parent about events. The application determines the child-window identifier; it must be unique for all child windows with the same parent window.

简单地说,控件通常不需要菜单,所以,可以用这个参数来设置控件的ID,反正hMenu闲着也没事干,就给个ID它玩玩。ID号是一个整数,不过为了可读性,一般是声明一个宏,其实我们在资源编辑器中使用的资源ID(如IDM_FUCK)就是在resource.h中定义的宏的,既然叫ID了,你就知道它的值不要重复。

我们也来模拟一下,在文件的前面(#include...后)也声明三个宏,分别标识三个按钮。

  1. #define IDB_ONE     3301
  2. #define IDB_TWO     3302
  3. #define IDB_THREE   3303

然后创建三个按钮:

  1. case WM_CREATE:
  2. {
  3. //创建三个按钮
  4. CreateWindow(L"Button", L"按钮一", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
  5. 35, 10, 120, 60, hwnd, (HMENU)IDB_ONE, hg_app, NULL);
  6. CreateWindow(L"Button", L"按钮二", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
  7. 35, 80, 120, 60, hwnd, (HMENU)IDB_TWO, hg_app, NULL);
  8. CreateWindow(L"Button", L"按钮三", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
  9. 35, 150, 120, 60, hwnd, (HMENU)IDB_THREE, hg_app, NULL);
  10. }
  11. return 0;

然后我们来响应WM_COMMAND消息。

  1. case WM_COMMAND:
  2. {
  3. switch(LOWORD(wParam))
  4. {
  5. case IDB_ONE:
  6. MessageBox(hwnd, L"您点击了第一个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  7. break;
  8. case IDB_TWO:
  9. MessageBox(hwnd, L"您点击了第二个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  10. break;
  11. case IDB_THREE:
  12. MessageBox(hwnd, L"您点击了第三个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  13. break;
  14. default:
  15. break;
  16. }
  17. }
  18. return 0;

看看效果。

这时候,我希望,当我点击了按钮后,按钮上的文本变成“按钮X已点击”,该怎么做呢?Windows系统是基于消息机制的,所以,首先想到,向控件发送消息,要改变控件相关的文本,应当发送WM_SETTEXT消息。

我们把上面的代码改一下。

  1. case WM_COMMAND:
  2. {
  3. switch(LOWORD(wParam))
  4. {
  5. case IDB_ONE:
  6. //MessageBox(hwnd, L"您点击了第一个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  7. SendMessage((HWND)lParam, WM_SETTEXT, (WPARAM)NULL, (LPARAM)L"第一个按鈕已点击");
  8. break;
  9. case IDB_TWO:
  10. //MessageBox(hwnd, L"您点击了第二个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  11. SendMessage((HWND)lParam, WM_SETTEXT, (WPARAM)NULL, (LPARAM)L"第二个按鈕已点击");
  12. break;
  13. case IDB_THREE:
  14. //MessageBox(hwnd, L"您点击了第三个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  15. SendMessage((HWND)lParam, WM_SETTEXT, (WPARAM)NULL, (LPARAM)L"第三个按鈕已点击");
  16. break;
  17. default:
  18. break;
  19. }
  20. }
  21. return 0;

前面我们知道,WM_COMMAND消息的lParam保存控件的句柄,所以,我们传给SendMessage的第一个参数是操作目标的句柄,注意,这里不要传WindowProc回调中的参数,因为我们现在要操作的对象是按钮,不是窗口,WindowProc传进到的句柄是指我们注册的窗口,因为我们在WNDCLASS中已经设定了该WindowProc函数。

要对按钮进行操作,应当使用WM_COMMAND的lParam中包含的值,强制转换为HWND。

运行结果如下图所示。

完整的示例如下:

  1. #include <Windows.h>
  2. #include <WinNT.h>
  3. //#include "resource.h"
  4. #define IDB_ONE     3301
  5. #define IDB_TWO     3302
  6. #define IDB_THREE   3303
  7. LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  8. LPCWSTR lps_cl = L"MyApp";//类名
  9. LPCWSTR wd_text = L"超级应用";//窗口标题
  10. HINSTANCE hg_app;//全局实例句柄
  11. int WINAPI wWinMain(HINSTANCE hThisApp,
  12. HINSTANCE hPrevApp,
  13. LPWSTR lpCmd,
  14. int nShow)
  15. {
  16. WNDCLASSEX wc = { };
  17. wc.cbSize = sizeof(WNDCLASSEX);
  18. wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  19. wc.hInstance = hThisApp;
  20. wc.lpfnWndProc = (WNDPROC)WindowProc;
  21. wc.lpszClassName = lps_cl;
  22. wc.style = CS_HREDRAW | CS_VREDRAW;
  23. RegisterClassEx(&wc);
  24. HWND hwnd = CreateWindowEx(WS_EX_WINDOWEDGE,
  25. lps_cl,
  26. wd_text,
  27. WS_OVERLAPPEDWINDOW,
  28. 20,
  29. 25,
  30. 400,
  31. 300,
  32. NULL,
  33. NULL,
  34. hThisApp,
  35. NULL);
  36. if(hwnd == NULL)
  37. return -1;
  38. ShowWindow(hwnd, nShow);
  39. UpdateWindow(hwnd);
  40. hg_app = hThisApp;
  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. switch(msg)
  52. {
  53. case WM_DESTROY:
  54. PostQuitMessage(0);
  55. return 0;
  56. case WM_CREATE:
  57. {
  58. //创建三个按钮
  59. CreateWindow(L"Button", L"按钮一", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
  60. 35, 10, 160, 60, hwnd, (HMENU)IDB_ONE, hg_app, NULL);
  61. CreateWindow(L"Button", L"按钮二", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
  62. 35, 80, 160, 60, hwnd, (HMENU)IDB_TWO, hg_app, NULL);
  63. CreateWindow(L"Button", L"按钮三", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
  64. 35, 150, 160, 60, hwnd, (HMENU)IDB_THREE, hg_app, NULL);
  65. }
  66. return 0;
  67. case WM_COMMAND:
  68. {
  69. switch(LOWORD(wParam))
  70. {
  71. case IDB_ONE:
  72. //MessageBox(hwnd, L"您点击了第一个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  73. SendMessage((HWND)lParam, WM_SETTEXT, (WPARAM)NULL, (LPARAM)L"第一个按鈕已点击");
  74. break;
  75. case IDB_TWO:
  76. //MessageBox(hwnd, L"您点击了第二个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  77. SendMessage((HWND)lParam, WM_SETTEXT, (WPARAM)NULL, (LPARAM)L"第二个按鈕已点击");
  78. break;
  79. case IDB_THREE:
  80. //MessageBox(hwnd, L"您点击了第三个按钮。", L"提示", MB_OK | MB_ICONINFORMATION);
  81. SendMessage((HWND)lParam, WM_SETTEXT, (WPARAM)NULL, (LPARAM)L"第三个按鈕已点击");
  82. break;
  83. default:
  84. break;
  85. }
  86. }
  87. return 0;
  88. default:
  89. return DefWindowProc(hwnd,msg,wParam,lParam);
  90. }
  91. return 0;
  92. }

跟我一起玩Win32开发(11):使用控件——先来耍一下按钮的更多相关文章

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

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

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

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

  3. python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推荐(二)

    在上一篇blog:python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推荐 中介绍了python中的tkinter的一些东西,你可能对tkinter有一定的了解了.这篇b ...

  4. Android零基础入门第17节:Android开发第一个控件,TextView属性和方法大全

    原文:Android零基础入门第17节:Android开发第一个控件,TextView属性和方法大全 前面简单学习了一些Android UI的一些基础知识,那么接下来我们一起来详细学习Android的 ...

  5. 基于Jquery WeUI的微信开发H5页面控件的经验总结(2)

    在微信开发H5页面的时候,往往借助于WeUI或者Jquery WeUI等基础上进行界面效果的开发,由于本人喜欢在Asp.net的Web界面上使用JQuery,因此比较倾向于使用 jQuery WeUI ...

  6. WinForm界面开发之布局控件"WeifenLuo.WinFormsUI.Docking"的使用

    WinForm界面开发之布局控件"WeifenLuo.WinFormsUI.Docking"的使用 转自:http://www.cnblogs.com/wuhuacong/arch ...

  7. Android开发之日历控件实现

    Android开发之日历控件实现:以下都是转载的. 日历控件 日历控件 日历控件 日历控件

  8. Qt 开发 MS VC 控件终极篇

    Qt 开发 MS VC 控件终极篇 1. 使用 MSVC2015 通过项目向导创建 Qt ActiveQt Server 解决方案 项目配置:以下文件需要修改 1. 项目属性页->项目属性-&g ...

  9. Android开发:文本控件详解——TextView(一)基本属性

    一.简单实例: 新建的Android项目初始自带的Hello World!其实就是一个TextView. 在activity_main.xml中可以新建TextView,从左侧组件里拖拽到右侧预览界面 ...

随机推荐

  1. 格式转换至yuv422转 yuv420

    //pYUV为422,yuv为420 /*ok! * brief:pyuv is yuv422sp srcIn, and yuv is yuv420p desOut  */ int YUV422To4 ...

  2. 谈谈java垃圾回收机制

    近期看了一些关于垃圾回收机制的文章,总结一下. 垃圾回收器回收程序不在使用的对象占用的内存,也就是对象不可达,比方说对象被置为null. 要回到java的垃圾回收机制,从下面三个方面去回答: 1.哪些 ...

  3. Spring Boot JPA 连接数据库

    本文将介绍怎样在Spring Boot project中加入JPA作为持久化方式. 改动 pom.xml 依赖 与上一篇介绍的 jdbc 不同的是 spring-boot-starter-jdbc 改 ...

  4. javascript模块化编程:CommonJS和AMD规范

    AMD规范,异步模块定义.与CommonJS规范齐名并列. 作用都是利于JavaScript的模块化编程. 模块化编程的好处就是: 1.可重用 2.独立 3.能解决加载的依赖性问题 4.能解决重复加载 ...

  5. 使用Microsoft Office 2007将文档转换为PDF

    点击帮助 输入关键词PDF后搜索 点击进入Save or convert to PDF or XPS 点击进入2007 Microsoft Office Add-in: Microsoft Save ...

  6. 【c++】map 迭代器删除演示样例

    C++ STL中的map是很常见的.通常我们用例如以下方式来遍历,而且删除map中的一些entry: map<int, int> mp; mp.insert(make_pair(1,1)) ...

  7. URL 下载

    package URL; import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import j ...

  8. G.易彰彪的一张表

    易彰彪最近有点奇怪,一向爱打游戏他最近居然盯着一张全是大小写字母的表在看,好像在找什么东西.他说,这是他女神给他的一张表,他需要回答女神的问题——在忽略大小写(即大写字母和小写字母视为同一字母)的情况 ...

  9. web自动化测试的自身特点

    1.web页面是出现的元素可能具有不确定性 2.不同操作系统上不同web浏览器之间的兼容性 3.web应用的高并发性和容错性 4.移动设备上web客户端兼容性,旋转下和各种触摸特性

  10. 【Codeforces 947A】 Primal Sport

    [题目链接] 点击打开链接 [算法] 不难看出,x1的范围是[x2-P(x2)+1,x2],x0的范围是[x1-P(x1)+1,x1] 我们可以先做一遍线性筛,然后暴力就可以了 [代码] #inclu ...