让我们与星光探索者一起,探索Windows吧!
探索绘图编程
GDI,Graphic Device Interface,Windows提供了大量API供我们绘制各种各样的图形。
Windows每个窗口都是绘制出来的,当窗口需要绘制的时候,产生WM_PAINT消息,进而我们可以绘制窗口。
HDC,即为设备上下文句柄,DC是设备windows特有的一个概念,我们需要使用他来绘图。
下面是简单的绘图代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #define _CRT_SECURE_NO_WARNINGS #include "MyWindowApplication/resource.h" #include <windows.h>
#pragma comment(lib, "user32.lib") #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "kernel32.lib")
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { static const TCHAR TITLE[] = TEXT("星光探索者"); static const TCHAR CLASSNAME[] = TEXT("MyStruct");
WNDCLASS wc{0}; wc.hInstance = hInstance; wc.lpfnWndProc = WinProc; wc.lpszClassName = CLASSNAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx( 0, CLASSNAME, TITLE,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
MSG msg{0}; while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
return 0; }
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: PostQuitMessage(0); return 0; case WM_PAINT: { PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
DrawIcon(hdc, 50, 50, LoadIcon(NULL, LPCTSTR(IDI_MYICON))); for (int i = 10; i != 40; ++i) { for (int j = 10; j != 40; ++j) { SetPixel(hdc, i, j, RGB(255, 0, 255)); } } EndPaint(hwnd, &ps); return 0; } } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
|
这样就可以简单的绘图了。只需BeginPaint获得HDC,然后调用相关的API(API直接查官网相关资源),然后再EndPaint,就可以了。但是并不是什么时候都可以绘图的,只有绘图消息和计时器消息发生时,绘图才会成功。
使用对话框
对话框是一种窗口,他主要用于向用户显示信息或等待用户输入时的一个小弹窗。如果我们快速制作一个Win32程序,使用对话框是一种便捷方法。
同样是这个熟悉的界面,右键项目,添加(Add),资源(Resource)
点击对话框(Dialog),之后VS显示了一个可视化的设计器。我们可以直接拖拽控件快速布局。大部分小伙伴都对很喜欢这种形式吧!
为了示范,星光探索者之后就直接写代码了。
对话框根据显示后是否阻止与其他窗口交互,可分为模态对话框和非模态对话框。
模态对话框:显示后阻止你创建的其他窗口交互
非模态对话框:显示后不阻止你创建的其他窗口交互
模态对话框的创建方式,调用DialogBox函数。这个函数是阻塞函数,返回值为对话框销毁时的返回值。参数如下:
1 2 3 4 5 6
| INT_PTR DialogBox( HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc );
|
非模态对话框创建方式,调用CreateDialog函数,参数如下,意义和上方相同,不多叙述。这个函数不是阻塞函数。
1 2 3 4 5 6
| void CreateDialog( HINSTANCE hInstance, LPCTSTR lpName, HWND hWndParent, DLGPROC lpDialogFunc );
|
DLGPROC是对话框过程函数的类型,为:
1
| typedef INT_PTR (CALLBACK* DLGPROC)(HWND, UINT, WPARAM, LPARAM);
|
实际上和窗口消息过程函数是一模一样的。但是这个对话框过程函数的返回值如果为FALSE,表明还需要默认处理函数处理,否则不调用默认处理函数处理。
当对话框开始创建未显示时,产生WM_INITDIALOG消息 ,不产生WM_CREATE消息。因此,对话框过程函数不应该处理WM_CREATE消息。各位感兴趣的小伙伴可验证一下。
模态对话框和非模态对话框的处理函数写法要求大不同。模态对话框销毁时使用EndDialog 函数,不可以使用DestroyWindow等函数;而非模态对话框销毁时使用DestroyWindow 函数。
要想处理对话框内部的控件的操作,需要处理 WM_COMMAND消息。控件被点击时,lParam为控制窗口的句柄,wParam的高字节为控件的消息,低字节为控件ID标识符
下面是一个模态对话框的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| #define _CRT_SECURE_NO_WARNINGS #include "resource.h" #include <windows.h>
#pragma comment(lib, "user32.lib") #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "kernel32.lib")
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { static const TCHAR TITLE[] = TEXT("星光探索者"); static const TCHAR CLASSNAME[] = TEXT("MyStruct");
WNDCLASS wc{0}; wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.hInstance = hInstance; wc.lpfnWndProc = WinProc; wc.lpszClassName = CLASSNAME; wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx( 0,CLASSNAME, TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
MSG msg{0}; while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
return 0; }
LRESULT CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: MessageBox(hwnd, TEXT("对话框被创建"), TEXT("探索对话框"), MB_OK); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: MessageBox(hwnd, TEXT("我也觉得OK"), TEXT("来自对话框的信息"), MB_OK); return TRUE; case IDCANCEL: EndDialog(hwnd, 0); return FALSE; } return FALSE; } return FALSE; }
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: PostQuitMessage(0); return 0; case WM_RBUTTONDOWN: DialogBox(NULL, (LPCTSTR)IDD_DIALOG1, hwnd, DialogProc); return 0; } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
|
运行这个实例,当我们鼠标右键的时候,弹出一个消息框后弹出对话框。点击OK按钮,将显示“我也觉得OK啊“;点击CANCEL按钮,将关闭对话框。
使用系统定义好的控件
对话框里能拖出这么多控件,实际上系统已经提前定义好了一些控件。
包含头文件CommCtrl.h
,这个头文件定义了很多定义好的系统窗口类 。但是你需要在#include <windows.h>
头文件后再#include <CommCtrl.h>
,因为你微软居然要用windows.h头文件,居然不包含,导致编译的时候提示有很多标识符未定义,不这么做会报错。
系统已经定义好的控件包括WC_BUTTON(按钮),WC_EDIT(文本编辑框),WC_STATIC(不可改文本控件),WC_TREEVIEW(树状图),ANIMATE_CLASS(动画)等。
窗口控件的消息,是通过WM_COMMAND消息间接传上来的。 控件被点击时,lParam为控制窗口的句柄,wParam的高字节为控件的消息,低字节为控件ID标识符。 这个在对话框消息过程函数已经提及。
看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| #define _CRT_SECURE_NO_WARNINGS #include <windows.h> #include <CommCtrl.h>
#pragma comment(lib, "user32.lib") #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "kernel32.lib")
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { static const TCHAR TITLE[] = TEXT("星光探索者"); static const TCHAR CLASSNAME[] = TEXT("MyStruct");
WNDCLASS wc{0}; wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.hInstance = hInstance; wc.lpfnWndProc = WinProc; wc.lpszClassName = CLASSNAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx( 0,CLASSNAME, TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
MSG msg{0}; while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
return 0; }
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: PostQuitMessage(0); return 0; case WM_CREATE: { HINSTANCE hInstance = GetModuleHandle(NULL); CreateWindowEx( 0, WC_BUTTON, TEXT("BUTTON"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 5, 5, 100, 100, hwnd, NULL, hInstance, NULL ); } case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED) { MessageBox(hwnd, TEXT("按钮被点击!"), TEXT("你好!"), MB_OK); return 0; } } return DefWindowProc(hwnd, uMsg, wParam, lParam); }
|
运行本程序,可以看到成功创建了一个按钮,并且点击之后会弹出“按钮被点击”的消息框。
阶段小结
经过了好几篇文章的探索,想必你对Win32程序设计有了更深一步的了解,可以继续探索Windows了。转眼间春节就要到了,星光探索者祝各位春节快乐!
本期到此结束!感谢各位小伙伴的阅读!