[toc]

在windows窗口程序中让图形通过键盘控制移动

1.通过WSAD控制像素点上下左右移动

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
#include <windows.h>

// 定义全局变量来存储红色像素点的坐标
int posX = 50; // 初始X坐标
int posY = 50; // 初始Y坐标

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 使用 SetPixel 绘制像素点
SetPixel(hdc, posX, posY, RGB(255, 0, 0)); // 只绘制一个红色像素点

EndPaint(hwnd, &ps);
} break;

case WM_KEYDOWN: {
// 根据按下的键更新坐标
switch (wParam) {
case 'W': // 向上移动
posY -= 5;
break;
case 'S': // 向下移动
posY += 5;
break;
case 'A': // 向左移动
posX -= 5;
break;
case 'D': // 向右移动
posX += 5;
break;
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "SetPixel Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

2.通过WSAD控制矩形上下左右移动

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
#include <windows.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_KEYDOWN: {
// 根据按下的键更新矩形的坐标
switch (wParam) {
case 'W': // 向上移动
rectY -= 5;
break;
case 'S': // 向下移动
rectY += 5;
break;
case 'A': // 向左移动
rectX -= 5;
break;
case 'D': // 向右移动
rectX += 5;
break;
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Rectangle Control Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

3.修复移动时会留下痕迹的问题

解决方法是每次重新绘制时先清除窗口背景再重新绘制

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
#include <windows.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 清除窗口背景
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_KEYDOWN: {
// 根据按下的键更新矩形的坐标
switch (wParam) {
case 'W': // 向上移动
rectY -= 5;
break;
case 'S': // 向下移动
rectY += 5;
break;
case 'A': // 向左移动
rectX -= 5;
break;
case 'D': // 向右移动
rectX += 5;
break;
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Rectangle Control Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

4.添加边界检测,使图形不突破边界

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
88
89
90
91
92
93
94
95
96
#include <windows.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 清除窗口背景
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_KEYDOWN: {
// 获取窗口的客户区大小
RECT clientRect;
GetClientRect(hwnd, &clientRect);

// 根据按下的键更新矩形的坐标
switch (wParam) {
case VK_UP: // 向上移动
if (rectY > 0) {
rectY -= 5;
}
break;
case VK_DOWN: // 向下移动
if (rectY + rectHeight < clientRect.bottom) {
rectY += 5;
}
break;
case VK_LEFT: // 向左移动
if (rectX > 0) {
rectX -= 5;
}
break;
case VK_RIGHT: // 向右移动
if (rectX + rectWidth < clientRect.right) {
rectX += 5;
}
break;
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Rectangle Control Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

5.添加左上、右上、右下和左下四个方向的移动

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <windows.h>

// 定义全局变量来存储矩形的坐标和大小
int rectX = 50; // 矩形左上角的X坐标
int rectY = 50; // 矩形左上角的Y坐标
int rectWidth = 50; // 矩形的宽度
int rectHeight = 30; // 矩形的高度

// 定义标志来跟踪按下的键
BOOL keyUpPressed = FALSE;
BOOL keyDownPressed = FALSE;
BOOL keyLeftPressed = FALSE;
BOOL keyRightPressed = FALSE;

// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// 清除窗口背景
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

// 绘制矩形
Rectangle(hdc, rectX, rectY, rectX + rectWidth, rectY + rectHeight);

EndPaint(hwnd, &ps);
} break;

case WM_KEYDOWN: {
// 获取窗口的客户区大小
RECT clientRect;
GetClientRect(hwnd, &clientRect);

// 根据按下的键更新标志
switch (wParam) {
case VK_UP: // 向上移动
keyUpPressed = TRUE;
if (rectY > 0) {
rectY -= 5;
}
break;
case VK_DOWN: // 向下移动
keyDownPressed = TRUE;
if (rectY + rectHeight < clientRect.bottom) {
rectY += 5;
}
break;
case VK_LEFT: // 向左移动
keyLeftPressed = TRUE;
if (rectX > 0) {
rectX -= 5;
}
break;
case VK_RIGHT: // 向右移动
keyRightPressed = TRUE;
if (rectX + rectWidth < clientRect.right) {
rectX += 5;
}
break;
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_KEYUP: {
// 根据释放的键更新标志
switch (wParam) {
case VK_UP:
keyUpPressed = FALSE;
break;
case VK_DOWN:
keyDownPressed = FALSE;
break;
case VK_LEFT:
keyLeftPressed = FALSE;
break;
case VK_RIGHT:
keyRightPressed = FALSE;
break;
}
} break;

case WM_TIMER: {
// 获取窗口的客户区大小
RECT clientRect;
GetClientRect(hwnd, &clientRect);

// 检查按键状态并移动矩形
if (keyUpPressed && keyRightPressed) { // 右上
if (rectX + rectWidth < clientRect.right && rectY > 0) {
rectX += 5; // 向右移动
rectY -= 5; // 向上移动
}
}
if (keyUpPressed && keyLeftPressed) { // 左上
if (rectX > 0 && rectY > 0) {
rectX -= 5; // 向左移动
rectY -= 5; // 向上移动
}
}
if (keyDownPressed && keyRightPressed) { // 右下
if (rectX + rectWidth < clientRect.right && rectY + rectHeight < clientRect.bottom) {
rectX += 5; // 向右移动
rectY += 5; // 向下移动
}
}
if (keyDownPressed && keyLeftPressed) { // 左下
if (rectX > 0 && rectY + rectHeight < clientRect.bottom) {
rectX -= 5; // 向左移动
rectY += 5; // 向下移动
}
}

// 触发重绘窗口
InvalidateRect(hwnd, NULL, TRUE);
} break;

case WM_DESTROY:
PostQuitMessage(0);
return 0;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}

// WinMain 函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const char CLASS_NAME[] = "Sample Window Class";

WNDCLASS wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Rectangle Control Example",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400,
NULL, NULL, hInstance, NULL
);

ShowWindow(hwnd, nShowCmd);

// 设置定时器以定期检查键状态
SetTimer(hwnd, 1, 50, NULL); // 每50毫秒检查一次

// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}