WindowsAPI窗口程序设计-常用控件

按钮类

本篇搞一个全是按钮的页面,从上到下分别是普通按钮、图标按钮、位图按钮和自绘按钮,接下来是三个分组框。第一个叫政治面貌,里面有一组单选按钮,分别是中共党员、共青团员、无党派人士。第二个叫个人爱好,里面有3个多选框,分别是看书、唱歌、听音乐。第三个叫荣誉称号,里面有三个三态多选框,即有对勾、有灰色对勾和无对勾,分别是团队核心、技术能手和先进个人。最后有个默认按钮为获取单选复选状态。

常用按钮样式:

枚举值 含义
BS_PUSHBUTTON 普通按钮
BS_NOTIFY 系统发送带有BN_KILLFOCUS和BN_SETFOCUS通知码的WM_COMMAND消息到父窗口
BS_ICON 图标按钮
BS_BITMAP 位图按钮
BS_OWNERDRAW 自绘按钮,按钮需要重绘时父窗口收到WM_DRAWITEM消息
BS_GROUPBOX 分组框
BS_AUTORADIOBUTTON 自动单选窗口
BS_AUTOCHECKBOX 自动复选框
BS_AUTO3STATE 自动三态复选框
BS_DEFPUSHBUTTON 默认按钮

常用属性如下,因为窗口等控件都统称窗口,属性即窗口样式:

枚举值 含义
WS_STOP 按下Tab时可接收键盘焦点
WS_GROUP 一组控件的第一个控件
WS_CHILD 子窗口
WS_VISIBLE 最初可见

例如:

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
// 全局变量
struct {
INT m_nStyle;
PTSTR m_pText;
}Buttons[] = {
BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, TEXT("普通按钮"), // CtrlID 1000
BS_ICON | BS_NOTIFY | WS_TABSTOP, TEXT("图标按钮"),
BS_BITMAP | BS_NOTIFY | WS_TABSTOP, TEXT("位图按钮"),
BS_OWNERDRAW, TEXT("自绘按钮"),

BS_GROUPBOX, TEXT("政治面貌"), // CtrlID 1004
BS_AUTORADIOBUTTON | BS_NOTIFY | WS_GROUP | WS_TABSTOP, TEXT("中共党员"),
BS_AUTORADIOBUTTON | BS_NOTIFY, TEXT("共青团员"),
BS_AUTORADIOBUTTON | BS_NOTIFY, TEXT("无党派人士"),

BS_GROUPBOX, TEXT("个人爱好"), // CtrlID 1008
BS_AUTOCHECKBOX | BS_NOTIFY | WS_GROUP | WS_TABSTOP, TEXT("看书"),
BS_AUTOCHECKBOX | BS_NOTIFY, TEXT("唱歌"),
BS_AUTOCHECKBOX | BS_NOTIFY, TEXT("听音乐"),

BS_GROUPBOX, TEXT("荣誉称号"), // CtrlID 1012
BS_AUTO3STATE | BS_NOTIFY | WS_GROUP | WS_TABSTOP, TEXT("团队核心"),
BS_AUTO3STATE | BS_NOTIFY, TEXT("技术能手"),
BS_AUTO3STATE | BS_NOTIFY, TEXT("先进个人"),

BS_DEFPUSHBUTTON | BS_NOTIFY | WS_TABSTOP, TEXT("默认按钮"), // CtrlID 1016
};
#define NUM (sizeof(Buttons) / sizeof(Buttons[0]))

创建17个按钮与分组框:

1
2
for (SIZE_T i = 0; i < NUM; i++)
hwndButton[i] = CreateWindowEx(0, TEXT("Button"), Buttons[i].m_pText, WS_CHILD | WS_VISIBLE | Buttons[i].m_nStyle, 20, arrPos[i], 150, 25, hwnd, (HMENU)(1000 + i), ((LPCREATESTRUCT)lParam)->hInstance, NULL);

调整分组框位置:

1
2
3
MoveWindow(hwndButton[4], 10, arrPos[4], 170, 115, TRUE);
MoveWindow(hwndButton[8], 10, arrPos[8], 170, 115, TRUE);
MoveWindow(hwndButton[12], 10, arrPos[12], 170, 115, TRUE);

父窗口向子窗口控件发送消息:

1
2
3
4
5
6
7
LRESULT SendDlgItemMessage(
HWND hDlg, //父窗口句柄
INT nIDDlgItem, //子窗口控件ID
UINT Msg,
WPARAM wParam,
LPARAM lParam
)

对于BS_ICON或BS_BITMAP样式的按钮,发送BM_SETIMAGE消息为按钮设置图标或位图,wParam指定图标IMAGE_ICON或IMAGE_BITMAP,lParam指定为图像句柄HICON或HBITMAP。

1
2
3
// 为图标按钮、位图按钮设置图标、位图
SendDlgItemMessage(hwnd, IDC_ICONBUTTON, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadImage(((LPCREATESTRUCT)lParam)->hInstance, MAKEINTRESOURCE(IDI_SMILE), IMAGE_ICON, 20, 20, LR_DEFAULTCOLOR));
SendDlgItemMessage(hwnd, IDC_BITMAPBUTTON, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)LoadBitmap(((LPCREATESTRUCT)lParam)->hInstance, MAKEINTRESOURCE(IDB_KONGLONG)));

其中LoadBitmap用于从指定模块中加载指定位图资源:

1
2
3
4
HBITMAP LoadBitmap(
_In_ HINSTANCE hInstance, //模块句柄
_In_ LPCTSTR lpBitmapName //位图资源名称
) //成功返回位图句柄 失败NULL

其中LoadImage用于加载图标、光标、位图等:

1
2
3
4
5
6
7
8
HANDLE WINAPI LoadImage(
_In_opt_ HINSTANCE hInst, //模块句柄
_In_ LPCTSTR lpszName, //要加载的图像名称
_In_ UINT uType, //要加载的图像类别 IMAGE_BITMAP IMAGE_ICON IMAGE_CURSOR
_In_ INT cxDesired, //图像宽度 单位像素 0实际宽度
_In_ INT xyDesired, //图像高度
_In_ UINT fuLoad //加载选项
) //成功返回新加载图像句柄 失败NULL

对于fuLoad参数有些常用 选项:

枚举值 含义
LR_DEFAULTCOLOR 默认值
LR_LOADFROMFILE 黑白方式加载
LR_SHARED 多次加载同一资源时直接返回先前返回的句柄,加载系统图标或光标时必须用这个

没指定LR_SHARED时,不再使用加载的图标、光标或位图时需要用DestroyIcon删除图标、DestroyCursor删除光标、DeleteObject删除位图。

设置子窗口控件文本:

1
2
3
4
5
BOOL WINAPI SetDlgItemText(
_In_ HWND hDlg, //父窗口句柄
_In_ INT nIDDlgItem, //子窗口控件ID
_In_ LPCTSTR lpString //字符串指针
)

例如:

1
2
// 设置默认按钮的文本
SetDlgItemText(hwnd, IDC_DEFPUSHBUTTON, TEXT("获取单选复选状态"));

CheckRadioButton设置单选按钮组,用CheckDlgButton设置复选按钮组和三态复选按钮组:

1
2
3
4
5
6
7
8
9
10
11
BOOL CheckDlgButton(
_In_ HWND hDlg, //父窗口句柄
_In_ INT nIDButton, //子窗口控件ID
_In_ UINT uCheck //选中状态
)
BOOL CheckRadioButton(
_In_ HWND hDlg, //父窗口句柄
_In_ INT nIDFirstButton, //组中第一个单选按钮ID
_In_ INT nIDLastButton, //组中最后一个单选按钮ID
_In_ INT nIDCheckButton //要设置选中的单选按钮ID
)

这俩函数实现方式都是用SendDlgItemMessage发送BM_SETCHECK消息,状态有:选中BST_CHECKED、取消选中BST_UNCHECKED、三态复选框灰色不确定状态BST_INDETERMINATE。

当用户单击按钮时,系统向按钮父窗口发送包含BN_CLICKED通知码的WM_COMMAND消息,对于自动单选按钮、自动复选框和自动三态复选框通常不需要处理该消息,自动设置即可,这里只实现默认按钮的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED) // 可以省略该判断
switch (LOWORD(wParam)) {
// 子窗口控件ID常量定义请参见resource.h,可以根据需要在此处理每个控件的点击事件
case IDC_PUSHBUTTON: break;
case IDC_ICONBUTTON: break;
case IDC_BITMAPBUTTON: break;
case IDC_OWNERDRAWBUTTON: break;
case IDC_AUTORADIOBUTTON1:break;
case IDC_AUTORADIOBUTTON2:break;
case IDC_AUTORADIOBUTTON3:break;
case IDC_AUTOCHECKBOX1: break;
case IDC_AUTOCHECKBOX2: break;
case IDC_AUTOCHECKBOX3: break;
case IDC_AUTO3STATE1: break;
case IDC_AUTO3STATE2: break;
case IDC_AUTO3STATE3: break;
case IDC_DEFPUSHBUTTON: OnDefPushButton(hwnd); break;
};
return 0;
};

IsDlgButtonChecked获取按钮选中状态,本质就是用SendDlgItemMessage发送BM_GETCHECK消息:

1
2
3
4
UINT IsDlgButtonChecked(
_In_ HWND hDlg, //父窗口句柄
_In_ INT nIDButton //子窗口控件ID
) //BST_CHECKED BST_UNCHECKED BST_INDETERMINATE

其中处理默认按钮的方法:

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
VOID OnDefPushButton(HWND hwnd) {
TCHAR szBuf[128] = { 0 };
if (IsDlgButtonChecked(hwnd, IDC_AUTORADIOBUTTON1) & BST_CHECKED)
StringCchCopy(szBuf, _countof(szBuf), TEXT("政治面貌:中共党员\n"));
if (IsDlgButtonChecked(hwnd, IDC_AUTORADIOBUTTON2) & BST_CHECKED)
StringCchCopy(szBuf, _countof(szBuf), TEXT("政治面貌:共青团员\n"));
if (IsDlgButtonChecked(hwnd, IDC_AUTORADIOBUTTON3) & BST_CHECKED)
StringCchCopy(szBuf, _countof(szBuf), TEXT("政治面貌:无党派人士\n"));
StringCchCat(szBuf, _countof(szBuf), TEXT("个人爱好:"));
if (IsDlgButtonChecked(hwnd, IDC_AUTOCHECKBOX1) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("看书 "));
if (IsDlgButtonChecked(hwnd, IDC_AUTOCHECKBOX2) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("唱歌 "));
if (IsDlgButtonChecked(hwnd, IDC_AUTOCHECKBOX3) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("听音乐"));
StringCchCat(szBuf, _countof(szBuf), TEXT("\n"));
StringCchCat(szBuf, _countof(szBuf), TEXT("荣誉称号:"));
if (IsDlgButtonChecked(hwnd, IDC_AUTO3STATE1) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("团队核心 "));
if (IsDlgButtonChecked(hwnd, IDC_AUTO3STATE2) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("技术能手 "));
if (IsDlgButtonChecked(hwnd, IDC_AUTO3STATE3) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("先进个人"));
StringCchCat(szBuf, _countof(szBuf), TEXT("\n"));
MessageBox(hwnd, szBuf, TEXT("个人简介汇总"), MB_OK);
return;
};

自绘按钮的处理:

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
case WM_DRAWITEM: {
lpDIS = (LPDRAWITEMSTRUCT)lParam;
// 先把按钮矩形填充为和窗口背景一致的白色,然后画一个黑色圆角矩形
SelectObject(lpDIS->hDC, GetStockObject(NULL_PEN));
SelectObject(lpDIS->hDC, GetStockObject(WHITE_BRUSH));
Rectangle(lpDIS->hDC, 0, 0, lpDIS->rcItem.right + 1, lpDIS->rcItem.bottom + 1);
SelectObject(lpDIS->hDC, GetStockObject(BLACK_BRUSH));
RoundRect(lpDIS->hDC, 0, 0, lpDIS->rcItem.right + 1, lpDIS->rcItem.bottom + 1, 20, 20);
// 当用户点击按钮的时候,绘制COLOR_HIGHLIGHT颜色的圆角矩形
if (lpDIS->itemState & ODS_SELECTED) {
SelectObject(lpDIS->hDC, GetSysColorBrush(COLOR_HIGHLIGHT));
RoundRect(lpDIS->hDC, 0, 0, lpDIS->rcItem.right + 1, lpDIS->rcItem.bottom + 1, 20, 20);
};
// 当按钮获得焦点的时候,可以绘制一个焦点矩形
if (lpDIS->itemState & ODS_FOCUS) {
InflateRect(&lpDIS->rcItem, -2, -2);
DrawFocusRect(lpDIS->hDC, &lpDIS->rcItem);
};
// 自绘按钮的文本,透明背景的白色文字
SetBkMode(lpDIS->hDC, TRANSPARENT);
SetTextColor(lpDIS->hDC, RGB(255, 255, 255));
DrawText(lpDIS->hDC, TEXT("自绘按钮"), _tcslen(TEXT("自绘按钮")),&lpDIS->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
// 恢复设备环境
SelectObject(lpDIS->hDC, GetStockObject(BLACK_PEN));
SelectObject(lpDIS->hDC, GetStockObject(WHITE_BRUSH));
return TRUE;
};

例如子窗口(控件)的可见与隐藏:

1
2
ShowWindow(hwndChild, SW_SHOW);
ShowWindow(hwndChild, SW_HIDE);

子窗口的启用与禁用方法:

1
2
3
4
BOOL WINAPI EnableWindow(
_In_ HWND hWnd, //窗口或子窗口控件句柄
_In_ BOOL bEnable //启用TRUE 禁用FALSE
)

每个按钮背景都是灰色的,但窗口背景是白色的,很难看。推荐把窗口背景也改成灰色,改按钮背景很麻烦:

1
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);

子窗口控件字体很难看,给子控件发送WM_SETFONT消息即可:

1
2
3
hFont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体"));
for (SIZE_T i = 0; i < NUM; i++)
SendMessage(hwndButton[i], WM_SETFONT, (WPARAM)hFont, FALSE);

其中SendMessageSendDlgItemMessage功能相同:

1
2
3
4
5
6
LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
)

源代码:

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
163
164
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
#include "resource.h"
// 函数声明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// 按下默认按钮
VOID OnDefPushButton(HWND hwnd);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) {
WNDCLASSEX wndclass;
TCHAR szAppName[] = TEXT("Buttons"); // 程序标题、窗口类名
HWND hwnd;
MSG msg;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = NULL;
RegisterClassEx(&wndclass);
hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 600, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0) != 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
};
return msg.wParam;
};
// 全局变量
struct {
INT m_nStyle;
PTSTR m_pText;
}Buttons[] = {
BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, TEXT("普通按钮"), // CtrlID 1000
BS_ICON | BS_NOTIFY | WS_TABSTOP, TEXT("图标按钮"),
BS_BITMAP | BS_NOTIFY | WS_TABSTOP, TEXT("位图按钮"),
BS_OWNERDRAW, TEXT("自绘按钮"),
BS_GROUPBOX, TEXT("政治面貌"), // CtrlID 1004
BS_AUTORADIOBUTTON | BS_NOTIFY | WS_GROUP | WS_TABSTOP, TEXT("中共党员"),
BS_AUTORADIOBUTTON | BS_NOTIFY, TEXT("共青团员"),
BS_AUTORADIOBUTTON | BS_NOTIFY, TEXT("无党派人士"),
BS_GROUPBOX, TEXT("个人爱好"), // CtrlID 1008
BS_AUTOCHECKBOX | BS_NOTIFY | WS_GROUP | WS_TABSTOP, TEXT("看书"),
BS_AUTOCHECKBOX | BS_NOTIFY, TEXT("唱歌"),
BS_AUTOCHECKBOX | BS_NOTIFY, TEXT("听音乐"),
BS_GROUPBOX, TEXT("荣誉称号"), // CtrlID 1012
BS_AUTO3STATE | BS_NOTIFY | WS_GROUP | WS_TABSTOP, TEXT("团队核心"),
BS_AUTO3STATE | BS_NOTIFY, TEXT("技术能手"),
BS_AUTO3STATE | BS_NOTIFY, TEXT("先进个人"),
BS_DEFPUSHBUTTON | BS_NOTIFY | WS_TABSTOP, TEXT("默认按钮"), // CtrlID 1016
};
#define NUM (sizeof(Buttons) / sizeof(Buttons[0]))
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HWND hwndButton[NUM]; // 子窗口控件句柄数组
INT arrPos[NUM] = { 10, 40, 70, 100,130, 150, 180, 210, 250, 270, 300, 330, 370, 390, 420, 450, 490 }; // 每个子窗口控件的起始Y坐标
LPDRAWITEMSTRUCT lpDIS;
switch (uMsg) {
case WM_CREATE: {
// 创建17个子窗口控件
for (SIZE_T i = 0; i < NUM; i++)
hwndButton[i] = CreateWindowEx(0, TEXT("Button"), Buttons[i].m_pText, WS_CHILD | WS_VISIBLE | Buttons[i].m_nStyle, 20, arrPos[i], 150, 25, hwnd, (HMENU)(1000 + i), ((LPCREATESTRUCT)lParam)->hInstance, NULL);
// 移动3个分组框的位置
MoveWindow(hwndButton[4], 10, arrPos[4], 170, 115, TRUE);
MoveWindow(hwndButton[8], 10, arrPos[8], 170, 115, TRUE);
MoveWindow(hwndButton[12], 10, arrPos[12], 170, 115, TRUE);
// 为图标按钮、位图按钮设置图标、位图
SendDlgItemMessage(hwnd, IDC_ICONBUTTON, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadImage(((LPCREATESTRUCT)lParam)->hInstance, MAKEINTRESOURCE(IDI_SMILE), IMAGE_ICON, 20, 20, LR_DEFAULTCOLOR));
SendDlgItemMessage(hwnd, IDC_BITMAPBUTTON, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)LoadBitmap(((LPCREATESTRUCT)lParam)->hInstance, MAKEINTRESOURCE(IDB_KONGLONG)));
// 设置默认按钮的文本
SetDlgItemText(hwnd, IDC_DEFPUSHBUTTON, TEXT("获取单选复选状态"));
// 单选按钮组、复选按钮组、三态复选按钮组,默认情况下分别选中一项
CheckRadioButton(hwnd, IDC_AUTORADIOBUTTON1, IDC_AUTORADIOBUTTON3, IDC_AUTORADIOBUTTON2);
CheckDlgButton(hwnd, IDC_AUTOCHECKBOX3, BST_CHECKED);
CheckDlgButton(hwnd, IDC_AUTO3STATE2, BST_INDETERMINATE);
return 0;
};
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED) // 可以省略该判断
switch (LOWORD(wParam)) {
// 子窗口控件ID常量定义请参见resource.h,可以根据需要在此处理每个控件的点击事件
case IDC_PUSHBUTTON: break;
case IDC_ICONBUTTON: break;
case IDC_BITMAPBUTTON: break;
case IDC_OWNERDRAWBUTTON: break;
case IDC_AUTORADIOBUTTON1:break;
case IDC_AUTORADIOBUTTON2:break;
case IDC_AUTORADIOBUTTON3:break;
case IDC_AUTOCHECKBOX1: break;
case IDC_AUTOCHECKBOX2: break;
case IDC_AUTOCHECKBOX3: break;
case IDC_AUTO3STATE1: break;
case IDC_AUTO3STATE2: break;
case IDC_AUTO3STATE3: break;
case IDC_DEFPUSHBUTTON: OnDefPushButton(hwnd); break;
};
return 0;
};
case WM_DRAWITEM: {
lpDIS = (LPDRAWITEMSTRUCT)lParam;
// 先把按钮矩形填充为和窗口背景一致的白色,然后画一个黑色圆角矩形
SelectObject(lpDIS->hDC, GetStockObject(NULL_PEN));
SelectObject(lpDIS->hDC, GetStockObject(WHITE_BRUSH));
Rectangle(lpDIS->hDC, 0, 0, lpDIS->rcItem.right + 1, lpDIS->rcItem.bottom + 1);
SelectObject(lpDIS->hDC, GetStockObject(BLACK_BRUSH));
RoundRect(lpDIS->hDC, 0, 0, lpDIS->rcItem.right + 1, lpDIS->rcItem.bottom + 1, 20, 20);
// 当用户点击按钮的时候,绘制COLOR_HIGHLIGHT颜色的圆角矩形
if (lpDIS->itemState & ODS_SELECTED) {
SelectObject(lpDIS->hDC, GetSysColorBrush(COLOR_HIGHLIGHT));
RoundRect(lpDIS->hDC, 0, 0, lpDIS->rcItem.right + 1, lpDIS->rcItem.bottom + 1, 20, 20);
};
// 当按钮获得焦点的时候,可以绘制一个焦点矩形
if (lpDIS->itemState & ODS_FOCUS) {
InflateRect(&lpDIS->rcItem, -2, -2);
DrawFocusRect(lpDIS->hDC, &lpDIS->rcItem);
};
// 自绘按钮的文本,透明背景的白色文字
SetBkMode(lpDIS->hDC, TRANSPARENT);
SetTextColor(lpDIS->hDC, RGB(255, 255, 255));
DrawText(lpDIS->hDC, TEXT("自绘按钮"), _tcslen(TEXT("自绘按钮")), &lpDIS->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
// 恢复设备环境
SelectObject(lpDIS->hDC, GetStockObject(BLACK_PEN));
SelectObject(lpDIS->hDC, GetStockObject(WHITE_BRUSH));
return TRUE;
};
case WM_DESTROY:
PostQuitMessage(0);
return 0;
};
return DefWindowProc(hwnd, uMsg, wParam, lParam);
};
VOID OnDefPushButton(HWND hwnd) {
TCHAR szBuf[128] = { 0 };
if (IsDlgButtonChecked(hwnd, IDC_AUTORADIOBUTTON1) & BST_CHECKED)
StringCchCopy(szBuf, _countof(szBuf), TEXT("政治面貌:中共党员\n"));
if (IsDlgButtonChecked(hwnd, IDC_AUTORADIOBUTTON2) & BST_CHECKED)
StringCchCopy(szBuf, _countof(szBuf), TEXT("政治面貌:共青团员\n"));
if (IsDlgButtonChecked(hwnd, IDC_AUTORADIOBUTTON3) & BST_CHECKED)
StringCchCopy(szBuf, _countof(szBuf), TEXT("政治面貌:无党派人士\n"));
StringCchCat(szBuf, _countof(szBuf), TEXT("个人爱好:"));
if (IsDlgButtonChecked(hwnd, IDC_AUTOCHECKBOX1) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("看书 "));
if (IsDlgButtonChecked(hwnd, IDC_AUTOCHECKBOX2) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("唱歌 "));
if (IsDlgButtonChecked(hwnd, IDC_AUTOCHECKBOX3) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("听音乐"));
StringCchCat(szBuf, _countof(szBuf), TEXT("\n"));
StringCchCat(szBuf, _countof(szBuf), TEXT("荣誉称号:"));
if (IsDlgButtonChecked(hwnd, IDC_AUTO3STATE1) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("团队核心 "));
if (IsDlgButtonChecked(hwnd, IDC_AUTO3STATE2) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("技术能手 "));
if (IsDlgButtonChecked(hwnd, IDC_AUTO3STATE3) & BST_CHECKED)
StringCchCat(szBuf, _countof(szBuf), TEXT("先进个人"));
StringCchCat(szBuf, _countof(szBuf), TEXT("\n"));
MessageBox(hwnd, szBuf, TEXT("个人简介汇总"), MB_OK);
return;
};

编辑/文本类

关于界面太难看的问题,可以这样解决,启动控件版本6:

1
2
3
#include <Commctrl.h>
#pragma comment(lib, "Comctl32.lib")
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

编辑控件的一些常用属性:

枚举值 含义
ES_AUTOHSCROLL 对于单行编辑器自动水平滚动
ES_PASSWORD 输入的字符显示为星号
ES_NUMBER 只输入数字

界面绘制:

1
2
3
4
5
6
7
8
9
10
11
12
13
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
hwndStatic[0] = CreateWindowEx(0, TEXT("Static"), TEXT("会员注册"), WS_CHILD | WS_VISIBLE | SS_CENTER, 130, 20, 80, 20, hwnd, (HMENU)(-1), hInstance, NULL);
// 用户名
hwndStatic[1] = CreateWindowEx(0, TEXT("Static"), TEXT("用户名:"), WS_CHILD | WS_VISIBLE, 20, 50, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndUserName = CreateWindowEx(0, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 80, 50, 220, 20, hwnd, (HMENU)(1000), hInstance, NULL);
// 密码
hwndStatic[2] = CreateWindowEx(0, TEXT("Static"), TEXT("密 码:"), WS_CHILD | WS_VISIBLE, 20, 75, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndPassword = CreateWindowEx(0, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_PASSWORD, 80, 75, 180, 20, hwnd, (HMENU)(1001), hInstance, NULL);
// 年龄
hwndStatic[3] = CreateWindowEx(0, TEXT("Static"), TEXT("年 龄:"), WS_CHILD | WS_VISIBLE, 20, 100, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndAge = CreateWindowEx(0, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_NUMBER, 80, 100, 180, 20, hwnd, (HMENU)(1002), hInstance, NULL);
// 注册按钮
hwndRegister = CreateWindowEx(0, TEXT("Button"), TEXT("注册"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, 85, 130, 150, 25, hwnd, (HMENU)(1003), hInstance, NULL);

改字体:

1
2
3
4
5
6
7
8
hFont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体"));
// 设置所有控件字体
SendMessage(hwndUserName, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndPassword, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndAge, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndRegister, WM_SETFONT, (WPARAM)hFont, FALSE);
for (SIZE_T i = 0; i < _countof(hwndStatic); i++)
SendMessage(hwndStatic[i], WM_SETFONT, (WPARAM)hFont, FALSE);

编辑框常用消息:

枚举值 含义
EM_SETLIMITTEXT 限制字符数
EM_LINELENGTH 获取指定行的字符数 不含终止空字符
EM_GETLINE 复制指定行文本并返回复制的字符数

限制输入框长度:

1
2
3
4
// 用户名、密码、年龄分别限制输入20、12、3个字符
SendMessage(hwndUserName, EM_SETLIMITTEXT, 20, 0);
SendMessage(hwndPassword, EM_SETLIMITTEXT, 12, 0);
SendMessage(hwndAge, EM_SETLIMITTEXT, 3, 0);

窗口坐标大小设定:

1
2
3
4
// 设置程序窗口大小,客户区所需最小大小为320 * 175
SetRect(&rect, 0, 0, 320, 175);
AdjustWindowRectEx(&rect, GetWindowLongPtr(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL, GetWindowLongPtr(hwnd, GWL_EXSTYLE));
SetWindowPos(hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE);

窗口变动时重新调整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
case WM_SIZE: {
if (LOWORD(lParam) > 320 && HIWORD(lParam) > 175) {
cx = (LOWORD(lParam) - 320) / 2;
cy = (HIWORD(lParam) - 175) / 2;
SetWindowPos(hwndStatic[0], NULL, 130 + cx, 20 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndStatic[1], NULL, 20 + cx, 50 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndStatic[2], NULL, 20 + cx, 75 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndStatic[3], NULL, 20 + cx, 100 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndUserName, NULL, 80 + cx, 50 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndPassword, NULL, 80 + cx, 75 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndAge, NULL, 80 + cx, 100 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndRegister, NULL, 85 + cx, 130 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
};
return 0;
};

对于EM_GETLINE消息来说,接收缓冲区的第一个字符必须为缓冲区长度。单行编辑控件不用wParam参数,多行编辑控件在wParam指定具体获取哪一行的,例如:

1
2
3
4
TCHAR szbuf[128] = { 0 };
szBuf[0] = 128;
nCount = SendMessage(hwndEdit, EM_GETLINE, 0, (LPARAM)szBuf); //单行
nCount = SendMessage(hwndEdit, EM_GETLINE, 2, (LPARAM)szBuf); //多行 要第3行的

编辑框的消息一般用于多行编辑控件,单行编辑控件还有更简单的方法,例如获取编辑框中字符串并转换为数值型返回:

1
2
3
4
5
6
UINT WINAPI GetDlgItemInt(
_In_ HWND hDlg, //父窗口句柄
_In_ INT nIDDlgItem, //编辑控件ID
_Out_opt_ PBOOL lpTranslated, //执行成功与否 成功TRUE 失败FALSE
_In_ BOOL bSigned //是否检查编辑控件中字符串开头有没有减号
)

单击按钮后:

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
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED) {
if (LOWORD(wParam) == 1003) {
INT nLen;
LPTSTR lpUserName, lpPassword;
TCHAR szBuf[64] = { 0 };
nLen = SendMessage(hwndUserName, EM_LINELENGTH, 0, 0);
if (nLen < 3) {
MessageBox(hwnd, TEXT("用户名至少需要3个字符"), TEXT("错误提示"), MB_OK);
return 0;
};
lpUserName = new TCHAR[nLen + 1];
ZeroMemory(lpUserName, (nLen + 1) * sizeof(TCHAR));
lpUserName[0] = nLen + 1;
SendMessage(hwndUserName, EM_GETLINE, 0, (LPARAM)lpUserName);
nLen = SendMessage(hwndPassword, EM_LINELENGTH, 0, 0);
if (nLen < 3) {
MessageBox(hwnd, TEXT("密码至少需要3个字符"), TEXT("错误提示"), MB_OK);
return 0;
};
lpPassword = new TCHAR[nLen + 1];
ZeroMemory(lpPassword, (nLen + 1) * sizeof(TCHAR));
lpPassword[0] = nLen + 1;
SendMessage(hwndPassword, EM_GETLINE, 0, (LPARAM)lpPassword);
wsprintf(szBuf, TEXT("用户名:%s\n密 码:%s\n年 龄:%d"), lpUserName, lpPassword, GetDlgItemInt(hwnd, 1002, NULL, FALSE));
MessageBox(hwnd, szBuf, TEXT("注册信息"), MB_OK);
delete[] lpUserName;
delete[] lpPassword;
};
};
return 0;
};

对于WM_CTLCOLORSTATIC消息来说,针对静态控件、只读或禁用的编辑控件,这里只是用GetSysColorBrush返回一个白色画刷的画刷句柄:

1
2
3
case WM_CTLCOLORSTATIC: {
return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
};

完整代码:

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
#include <Windows.h>
#include <Commctrl.h>
#pragma comment(lib, "Comctl32.lib")
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// 函数声明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) {
WNDCLASSEX wndclass;
TCHAR szAppName[] = TEXT("EditDemo");
HWND hwnd;
MSG msg;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = NULL;
RegisterClassEx(&wndclass);
hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0) != 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
};
return msg.wParam;
};
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HINSTANCE hInstance;
static HWND hwndUserName, hwndPassword, hwndAge, hwndRegister;
static HWND hwndStatic[4];
static HFONT hFont;
int cx, cy;
RECT rect;
switch (uMsg) {
case WM_CREATE: {
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
hFont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体"));
hwndStatic[0] = CreateWindowEx(0, TEXT("Static"), TEXT("会员注册"), WS_CHILD | WS_VISIBLE | SS_CENTER, 130, 20, 80, 20, hwnd, (HMENU)(-1), hInstance, NULL);
// 用户名
hwndStatic[1] = CreateWindowEx(0, TEXT("Static"), TEXT("用户名:"), WS_CHILD | WS_VISIBLE, 20, 50, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndUserName = CreateWindowEx(0, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 80, 50, 220, 20, hwnd, (HMENU)(1000), hInstance, NULL);
// 密码
hwndStatic[2] = CreateWindowEx(0, TEXT("Static"), TEXT("密 码:"), WS_CHILD | WS_VISIBLE, 20, 75, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndPassword = CreateWindowEx(0, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_PASSWORD, 80, 75, 180, 20, hwnd, (HMENU)(1001), hInstance, NULL);
// 年龄
hwndStatic[3] = CreateWindowEx(0, TEXT("Static"), TEXT("年 龄:"), WS_CHILD | WS_VISIBLE, 20, 100, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndAge = CreateWindowEx(0, TEXT("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_NUMBER, 80, 100, 180, 20, hwnd, (HMENU)(1002), hInstance, NULL);
// 注册按钮
hwndRegister = CreateWindowEx(0, TEXT("Button"), TEXT("注册"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, 85, 130, 150, 25, hwnd, (HMENU)(1003), hInstance, NULL);
// 设置所有控件字体
SendMessage(hwndUserName, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndPassword, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndAge, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndRegister, WM_SETFONT, (WPARAM)hFont, FALSE);
for (SIZE_T i = 0; i < _countof(hwndStatic); i++)
SendMessage(hwndStatic[i], WM_SETFONT, (WPARAM)hFont, FALSE);
// 用户名、密码、年龄分别限制输入20、12、3个字符
SendMessage(hwndUserName, EM_SETLIMITTEXT, 20, 0);
SendMessage(hwndPassword, EM_SETLIMITTEXT, 12, 0);
SendMessage(hwndAge, EM_SETLIMITTEXT, 3, 0);
// 设置程序窗口大小,客户区所需最小大小为320 * 175
SetRect(&rect, 0, 0, 320, 175);
AdjustWindowRectEx(&rect, GetWindowLongPtr(hwnd, GWL_STYLE), GetMenu(hwnd) != NULL, GetWindowLongPtr(hwnd, GWL_EXSTYLE));
SetWindowPos(hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE);
return 0;
};
case WM_SIZE: {
if (LOWORD(lParam) > 320 && HIWORD(lParam) > 175) {
cx = (LOWORD(lParam) - 320) / 2;
cy = (HIWORD(lParam) - 175) / 2;
SetWindowPos(hwndStatic[0], NULL, 130 + cx, 20 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndStatic[1], NULL, 20 + cx, 50 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndStatic[2], NULL, 20 + cx, 75 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndStatic[3], NULL, 20 + cx, 100 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndUserName, NULL, 80 + cx, 50 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndPassword, NULL, 80 + cx, 75 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndAge, NULL, 80 + cx, 100 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
SetWindowPos(hwndRegister, NULL, 85 + cx, 130 + cy, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
};
return 0;
};
case WM_COMMAND: {
if (HIWORD(wParam) == BN_CLICKED) {
if (LOWORD(wParam) == 1003) {
INT nLen;
LPTSTR lpUserName, lpPassword;
TCHAR szBuf[64] = { 0 };
nLen = SendMessage(hwndUserName, EM_LINELENGTH, 0, 0);
if (nLen < 3) {
MessageBox(hwnd, TEXT("用户名至少需要3个字符"), TEXT("错误提示"), MB_OK);
return 0;
};
lpUserName = new TCHAR[nLen + 1];
ZeroMemory(lpUserName, (nLen + 1) * sizeof(TCHAR));
lpUserName[0] = nLen + 1;
SendMessage(hwndUserName, EM_GETLINE, 0, (LPARAM)lpUserName);
nLen = SendMessage(hwndPassword, EM_LINELENGTH, 0, 0);
if (nLen < 3) {
MessageBox(hwnd, TEXT("密码至少需要3个字符"), TEXT("错误提示"), MB_OK);
return 0;
};
lpPassword = new TCHAR[nLen + 1];
ZeroMemory(lpPassword, (nLen + 1) * sizeof(TCHAR));
lpPassword[0] = nLen + 1;
SendMessage(hwndPassword, EM_GETLINE, 0, (LPARAM)lpPassword);
wsprintf(szBuf, TEXT("用户名:%s\n密 码:%s\n年 龄:%d"), lpUserName, lpPassword, GetDlgItemInt(hwnd, 1002, NULL, FALSE));
MessageBox(hwnd, szBuf, TEXT("注册信息"), MB_OK);
delete[] lpUserName;
delete[] lpPassword;
};
};
return 0;
};
case WM_CTLCOLORSTATIC: {
return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
};
case WM_DESTROY: {
DeleteObject(hFont);
PostQuitMessage(0);
return 0;
};
};
return DefWindowProc(hwnd, uMsg, wParam, lParam);
};

列表框

列表框的常用样式就一个LBS_NOTIFY,当用户单击、双击或取消选中列表框时,向父窗口分别发送包含LBN_SELCHANGE、LBN_DBLCLK、LBN_SELCANCEL通知码的WM_COMMAND消息。

界面布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
// 列表框控件
hwndListBox = CreateWindowEx(0, TEXT("ListBox"), NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY,20, 20, 200, 116, hwnd, (HMENU)IDC_LISTBOX, hInstance, NULL);
// 静态控件和编辑框控件,文本:文本编辑框,项目数据:项目数据编辑框
hwndStaticText = CreateWindowEx(0, TEXT("Static"), TEXT("文本:"),WS_CHILD | WS_VISIBLE, 20, 144, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndEditText = CreateWindowEx(0, TEXT("Edit"), NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,80, 140, 140, 22, hwnd, (HMENU)IDC_EDITTEXT, hInstance, NULL);
hwndStaticData = CreateWindowEx(0, TEXT("Static"), TEXT("项目数据:"), WS_CHILD | WS_VISIBLE, 20, 169, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndEditData = CreateWindowEx(0, TEXT("Edit"), NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_NUMBER,80, 165, 140, 22, hwnd, (HMENU)IDC_EDITDATA, hInstance, NULL);
// 三个按钮,添加、修改、删除
hwndAdd = CreateWindowEx(0, TEXT("Button"), TEXT("添加"),WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY,20, 200, 60, 25, hwnd, (HMENU)IDC_BTNADD, hInstance, NULL);
hwndModify = CreateWindowEx(0, TEXT("Button"), TEXT("修改"),WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY,90, 200, 60, 25, hwnd, (HMENU)IDC_BTNMODIFY, hInstance, NULL);
hwndDelete = CreateWindowEx(0, TEXT("Button"), TEXT("删除"),WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY,160, 200, 60, 25, hwnd, (HMENU)IDC_BTNDELETE, hInstance, NULL);

普通列表框可以变成拖动列表框,列表项可以被鼠标拖拽并改变位置:

1
2
3
4
// 更改为拖动列表框 并注册WM_DRAGLIST消息
static UINT WM_DRAGLIST;
MakeDragList(hwndListBox);
WM_DRAGLIST = RegisterWindowMessage(DRAGLISTMSGSTRING);

列表框常用消息:

枚举值 含义
LB_ADDSTRING 添加一个字符串类型列表项
LB_SETITEMDATA 为一个列表项设置项目数据
LB_GETITEMDATA 获取与一个列表项关联的项目数据
LB_SETCURSEL 选中一个列表项
LB_GETCURSEL 获取当前选中项的索引
LB_DELETESTRING 删除一个列表项
LB_INSERTSTRING 插入指定位置
LB_GETCOUNT 获取列表框列表项总数
LB_GETTEXT 获取指定列表项字符串文本

对于LB_ADDSTRING消息,添加到列表末尾,wParam参数缺省,lParam为字符串指针,返回新添加列表项索引。发生错误返回LB_ERR,内存空间不够返回LB_ERRSPACE,例如:

1
nIndex = SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("汇编语言"));

在这里添加一堆表项:

1
2
3
4
5
6
7
8
9
// 添加一些列表项
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("汇编语言"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("C语言面向过程"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("C++面向对象"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("Windows程序设计"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("Ollydbg调试工具"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("X64Dbg调试工具"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("IDA静态调试"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("PE Explorer"));

对于LB_SETITEMDATA消息,wParam指定列表项索引,lParam缺省,发生错误返回LB_ERR。

1
nResult = SendMessage(hwndListBox, LB_SETITEMDATA, 2, (LPARAM)lpData);

例如:

1
2
3
// 设置项目数据,具体编程中可以根据需要添加所需的项目数据,例如某些自定义数据的指针
for (SIZE_T i = 0; i < 8; i++)
SendMessage(hwndListBox, LB_SETITEMDATA, i, 10000 + i);

对于LB_SETCURSEL消息,wParam指定列表项索引,lParam缺省,发生错误返回LB_ERR:

1
nCount = SendMessage(hwndListBox, LB_GETTEXTLEN, 2, 0);

对于LB_GETCURSEL消息,wParam和lParam都缺省,没有选中项返回LB_ERR。

1
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);

对于LB_DELETESTRING消息,wParam为列表项索引,lParam缺省,返回剩余列表项个数,发生错误返回LB_ERR。

1
nCount = SendMessage(hwndListBox, LB_DELETESTRING, 2, 0);

对于LB_INSERTSTRING消息,wParam指定插入位置索引,-1表示列表末尾,返回新添加列表项索引。发生错误返回LB_ERR,内存不够返回LB_ERRSPACE。

1
nIndex = SendMessage(hwndListBox, LB_INSERTSTRING, 1, (LPARAM)TEXT("汇编语言"));

对于LB_GETCOUNT消息,wParam和lParam都缺省,错误返回LB_ERR。

例如增改查操作:

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
case BN_CLICKED: {
// 添加
if (LOWORD(wParam) == IDC_BTNADD) {
nCount = GetDlgItemText(hwnd, IDC_EDITTEXT, szBuf, _countof(szBuf));
if (nCount > 0) {
nIndex = SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);
if (nIndex >= 0) {
dwData = GetDlgItemInt(hwnd, IDC_EDITDATA, NULL, TRUE);
SendMessage(hwndListBox, LB_SETITEMDATA, nIndex, dwData);
SendMessage(hwndListBox, LB_SETCURSEL, nIndex, 0);
EnableWindow(hwndModify, TRUE);
EnableWindow(hwndDelete, TRUE);
};
};
}
// 修改
else if (LOWORD(wParam) == IDC_BTNMODIFY) {
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
nCount = GetDlgItemText(hwnd, IDC_EDITTEXT, szBuf, _countof(szBuf));
if (nIndex >= 0 && nCount > 0) {
SendMessage(hwndListBox, LB_DELETESTRING, nIndex, 0);
SendMessage(hwndListBox, LB_INSERTSTRING, nIndex, (LPARAM)szBuf);
dwData = GetDlgItemInt(hwnd, IDC_EDITDATA, NULL, TRUE);
SendMessage(hwndListBox, LB_SETITEMDATA, nIndex, dwData);
SendMessage(hwndListBox, LB_SETCURSEL, nIndex, 0);
};
}
// 删除
else if (LOWORD(wParam) == IDC_BTNDELETE) {
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
if (nIndex >= 0) {
SendMessage(hwndListBox, LB_DELETESTRING, nIndex, 0);
nCount = SendMessage(hwndListBox, LB_GETCOUNT, 0, 0);
if (nCount > 0) {
if (nIndex < nCount)
SendMessage(hwndListBox, LB_SETCURSEL, nIndex, 0);
else
SendMessage(hwndListBox, LB_SETCURSEL, nIndex - 1, 0);
}
else {
EnableWindow(hwndModify, FALSE);
EnableWindow(hwndDelete, FALSE);
};
};
};
break;
};

对于LB_GETTEXT消息,wParam指定列表项索引,lParam为字符串缓冲区,返回字符个数,不含终止空字符。错误返回LB_ERR:

1
nCount = SendMessage(hwndListBox, LB_GETTEXT, 2, (LPARAM)szBuf);

对于LB_GETITEMDATA消息,wParam指定列表项索引,lParam缺省,返回指定列表项关联的项目数据。错误返回LB_ERR:

1
lpData = (LPVOID)SendMessage(hwndListBox, LB_GETITEMDATA, 2, 0);

更改所选中的列表项时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 列表框中的选择已更改
case LBN_SELCHANGE: {
if (LOWORD(wParam) == IDC_LISTBOX) {
// 获取列表项文本和项目数据,显示到编辑框中
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
SendMessage(hwndListBox, LB_GETTEXT, nIndex, (LPARAM)szBuf);
SetDlgItemText(hwnd, IDC_EDITTEXT, szBuf);
dwData = SendMessage(hwndListBox, LB_GETITEMDATA, nIndex, 0);
SetDlgItemInt(hwnd, IDC_EDITDATA, dwData, TRUE);
// 启用修改、删除按钮
EnableWindow(hwndModify, TRUE);
EnableWindow(hwndDelete, TRUE);
};
break;
};

编辑矿建内容变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 编辑控件的内容已变化
case EN_UPDATE: {
// 项目数据编辑框不需要检查有无内容,如果没有内容就是0
if (LOWORD(wParam) == IDC_EDITTEXT) {
nCount = GetDlgItemText(hwnd, IDC_EDITTEXT, szBuf, _countof(szBuf));
if (nCount > 0) {
// 添加按钮
EnableWindow(hwndAdd, TRUE);
// 修改按钮
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
if (nIndex >= 0)
EnableWindow(hwndModify, TRUE);
}
else {
// 添加按钮
EnableWindow(hwndAdd, FALSE);
// 修改按钮
EnableWindow(hwndModify, FALSE);
};
};
break;
};

至于拖动部分的处理不讲。

源代码:

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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#include <Windows.h>
#include <CommCtrl.h>
#include "resource.h"
#pragma comment(lib, "Comctl32.lib")
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// 函数声明
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) {
WNDCLASSEX wndclass;
TCHAR szAppName[] = TEXT("ListBoxDemo");
HWND hwnd;
MSG msg;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = NULL;
RegisterClassEx(&wndclass);
hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 256, 280, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0) != 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
};
return msg.wParam;
};
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
HINSTANCE hInstance;
static HWND hwndListBox, hwndEditText, hwndEditData, hwndAdd, hwndModify, hwndDelete;
HWND hwndStaticText, hwndStaticData;
static HFONT hFont;
static UINT WM_DRAGLIST;
TCHAR szBuf[64] = { 0 };
INT nIndex, nCount;
DWORD dwData;
switch (uMsg){
case WM_CREATE: {
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
// 列表框控件
hwndListBox = CreateWindowEx(0, TEXT("ListBox"), NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY,20, 20, 200, 116, hwnd, (HMENU)IDC_LISTBOX, hInstance, NULL);
// 静态控件和编辑框控件,文本:文本编辑框,项目数据:项目数据编辑框
hwndStaticText = CreateWindowEx(0, TEXT("Static"), TEXT("文本:"),WS_CHILD | WS_VISIBLE, 20, 144, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndEditText = CreateWindowEx(0, TEXT("Edit"), NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,80, 140, 140, 22, hwnd, (HMENU)IDC_EDITTEXT, hInstance, NULL);
hwndStaticData = CreateWindowEx(0, TEXT("Static"), TEXT("项目数据:"), WS_CHILD | WS_VISIBLE, 20, 169, 60, 20, hwnd, (HMENU)(-1), hInstance, NULL);
hwndEditData = CreateWindowEx(0, TEXT("Edit"), NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_NUMBER,80, 165, 140, 22, hwnd, (HMENU)IDC_EDITDATA, hInstance, NULL);
// 三个按钮,添加、修改、删除
hwndAdd = CreateWindowEx(0, TEXT("Button"), TEXT("添加"),WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY,20, 200, 60, 25, hwnd, (HMENU)IDC_BTNADD, hInstance, NULL);
hwndModify = CreateWindowEx(0, TEXT("Button"), TEXT("修改"),WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY,90, 200, 60, 25, hwnd, (HMENU)IDC_BTNMODIFY, hInstance, NULL);
hwndDelete = CreateWindowEx(0, TEXT("Button"), TEXT("删除"),WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_NOTIFY,160, 200, 60, 25, hwnd, (HMENU)IDC_BTNDELETE, hInstance, NULL);
// 更改为拖动列表框
MakeDragList(hwndListBox);
WM_DRAGLIST = RegisterWindowMessage(DRAGLISTMSGSTRING);
// 添加一些列表项
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("汇编语言"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("C语言面向过程"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("C++面向对象"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("Windows程序设计"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("Ollydbg调试工具"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("X64Dbg调试工具"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("IDA静态调试"));
SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)TEXT("PE Explorer"));
// 设置项目数据,具体编程中可以根据需要添加所需的项目数据,例如某些自定义数据的指针
for (SIZE_T i = 0; i < 8; i++)
SendMessage(hwndListBox, LB_SETITEMDATA, i, 10000 + i);
// 限制列表项文本最多可以输入20个字符,项目数据最多可以输入10个数字字符
SendMessage(hwndEditText, EM_SETLIMITTEXT, 20, 0);
SendMessage(hwndEditData, EM_SETLIMITTEXT, 10, 0);
// 设置字体
hFont = CreateFont(18, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("微软雅黑"));
SendMessage(hwndListBox, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndStaticText, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndStaticData, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndEditText, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndEditData, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndAdd, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndModify, WM_SETFONT, (WPARAM)hFont, FALSE);
SendMessage(hwndDelete, WM_SETFONT, (WPARAM)hFont, FALSE);
// 禁用添加、修改、删除按钮
EnableWindow(hwndAdd, FALSE);
EnableWindow(hwndModify, FALSE);
EnableWindow(hwndDelete, FALSE);
return 0;
};
case WM_CTLCOLORSTATIC: {
return (LRESULT)GetSysColorBrush(COLOR_WINDOW);
};
case WM_COMMAND: {
switch (HIWORD(wParam)) {
case BN_CLICKED: {
// 添加
if (LOWORD(wParam) == IDC_BTNADD) {
nCount = GetDlgItemText(hwnd, IDC_EDITTEXT, szBuf, _countof(szBuf));
if (nCount > 0) {
nIndex = SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);
if (nIndex >= 0) {
dwData = GetDlgItemInt(hwnd, IDC_EDITDATA, NULL, TRUE);
SendMessage(hwndListBox, LB_SETITEMDATA, nIndex, dwData);
SendMessage(hwndListBox, LB_SETCURSEL, nIndex, 0);
EnableWindow(hwndModify, TRUE);
EnableWindow(hwndDelete, TRUE);
};
};
}
// 修改
else if (LOWORD(wParam) == IDC_BTNMODIFY) {
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
nCount = GetDlgItemText(hwnd, IDC_EDITTEXT, szBuf, _countof(szBuf));
if (nIndex >= 0 && nCount > 0) {
SendMessage(hwndListBox, LB_DELETESTRING, nIndex, 0);
SendMessage(hwndListBox, LB_INSERTSTRING, nIndex, (LPARAM)szBuf);
dwData = GetDlgItemInt(hwnd, IDC_EDITDATA, NULL, TRUE);
SendMessage(hwndListBox, LB_SETITEMDATA, nIndex, dwData);
SendMessage(hwndListBox, LB_SETCURSEL, nIndex, 0);
};
}
// 删除
else if (LOWORD(wParam) == IDC_BTNDELETE) {
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
if (nIndex >= 0) {
SendMessage(hwndListBox, LB_DELETESTRING, nIndex, 0);
nCount = SendMessage(hwndListBox, LB_GETCOUNT, 0, 0);
if (nCount > 0) {
if (nIndex < nCount)
SendMessage(hwndListBox, LB_SETCURSEL, nIndex, 0);
else
SendMessage(hwndListBox, LB_SETCURSEL, nIndex - 1, 0);
}
else {
EnableWindow(hwndModify, FALSE);
EnableWindow(hwndDelete, FALSE);
};
};
};
break;
};
// 列表框中的选择已更改
case LBN_SELCHANGE: {
if (LOWORD(wParam) == IDC_LISTBOX) {
// 获取列表项文本和项目数据,显示到编辑框中
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
SendMessage(hwndListBox, LB_GETTEXT, nIndex, (LPARAM)szBuf);
SetDlgItemText(hwnd, IDC_EDITTEXT, szBuf);
dwData = SendMessage(hwndListBox, LB_GETITEMDATA, nIndex, 0);
SetDlgItemInt(hwnd, IDC_EDITDATA, dwData, TRUE);
// 启用修改、删除按钮
EnableWindow(hwndModify, TRUE);
EnableWindow(hwndDelete, TRUE);
};
break;
};
// 编辑控件的内容已变化
case EN_UPDATE: {
// 项目数据编辑框不需要检查有无内容,如果没有内容就是0
if (LOWORD(wParam) == IDC_EDITTEXT) {
nCount = GetDlgItemText(hwnd, IDC_EDITTEXT, szBuf, _countof(szBuf));
if (nCount > 0) {
// 添加按钮
EnableWindow(hwndAdd, TRUE);
// 修改按钮
nIndex = SendMessage(hwndListBox, LB_GETCURSEL, 0, 0);
if (nIndex >= 0)
EnableWindow(hwndModify, TRUE);
}
else {
// 添加按钮
EnableWindow(hwndAdd, FALSE);
// 修改按钮
EnableWindow(hwndModify, FALSE);
};
};
break;
};
};
return 0;
};
case WM_DESTROY: {
DeleteObject(hFont);
PostQuitMessage(0);
return 0;
};
};
if (uMsg == WM_DRAGLIST) {
LPDRAGLISTINFO pDragInfo = (LPDRAGLISTINFO)lParam;
static INT nIndexDrag;
switch (pDragInfo->uNotification) {
case DL_BEGINDRAG: {
nIndexDrag = LBItemFromPt(hwndListBox, pDragInfo->ptCursor, FALSE);
return TRUE;
};
case DL_DRAGGING: {
nIndex = LBItemFromPt(hwndListBox, pDragInfo->ptCursor, TRUE);
if (nIndex >= 0)
DrawInsert(hwnd, hwndListBox, nIndex);
return DL_MOVECURSOR;
};
case DL_DROPPED: {
nIndex = LBItemFromPt(hwndListBox, pDragInfo->ptCursor, FALSE);
if (nIndex >= 0 && nIndex != nIndexDrag) {
SendMessage(hwndListBox, LB_GETTEXT, nIndexDrag, (LPARAM)szBuf);
dwData = SendMessage(hwndListBox, LB_GETITEMDATA, nIndexDrag, 0);
if (nIndexDrag > nIndex) {
// 先删除再插入
SendMessage(hwndListBox, LB_DELETESTRING, nIndexDrag, 0);
SendMessage(hwndListBox, LB_INSERTSTRING, nIndex, (LPARAM)szBuf);
SendMessage(hwndListBox, LB_SETITEMDATA, nIndex, dwData);
SendMessage(hwndListBox, LB_SETCURSEL, nIndex, 0);
}
else {
// 先插入再删除
SendMessage(hwndListBox, LB_INSERTSTRING, nIndex, (LPARAM)szBuf);
SendMessage(hwndListBox, LB_SETITEMDATA, nIndex, dwData);
SendMessage(hwndListBox, LB_SETCURSEL, nIndex, 0);
SendMessage(hwndListBox, LB_DELETESTRING, nIndexDrag, 0);
};
};
// 隐藏插入图标
DrawInsert(hwnd, hwndListBox, -1);
nIndexDrag = -1;
return 0;
};
};
};
return DefWindowProc(hwnd, uMsg, wParam, lParam);
};