【第五节】windows sdk编程:windows 控件基础
目录
一、控件概述
二、标准控件
三、通用控件
四、控件的创建
五、控件风格
六、控件相关的消息
6.1 控件控制消息
6.2 控件通知消息
一、控件概述
控件是 Windows 系统内置的窗口类,它们只能是某个窗口的子窗口。因此,创建控件时必须使用 `WS_CHILD` 风格。控件的窗口类由系统预先注册,因此我们不需要手动注册窗口类。控件的消息响应函数由 Windows 提供,而不是我们自己编写的。
控件的行为由其内置的窗口回调函数处理。当控件发生某些行为时(如被点击或需要重绘),它会向其父窗口发送通知消息。
二、标准控件
标准控件是 Windows 提供的基本控件,使用方式简单。以下是常用的标准控件:
三、通用控件
通用控件是 Windows 提供的较为复杂的控件,例如树形控件、列表框控件、选项步控件等。以下是常见的通用控件:
四、控件的创建
控件的创建使用 `CreateWindow` 函数,但不需要手动注册窗口类。通常需要为控件指定 `WS_CHILD` 和 `WS_VISIBLE` 风格。由于子窗口没有菜单,`CreateWindow` 的菜单参数用于指定子窗口的 ID,以区分同一父窗口上的不同控件。
示例代码(标准与通用控件)
#include <windows.h>
#include <commctrl.h> // 公共控件头文件,包含列表视图、树形视图等控件的定义
// 窗口过程函数,用于处理窗口消息
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE: // 窗口创建时触发
{
// 创建列表视图控件
HWND hListView = CreateWindow(
WC_LISTVIEW, // 列表视图的窗口类名
L"ListView", // 控件初始文本
WS_VISIBLE | WS_CHILD | LVS_REPORT, // 控件风格:可见、子窗口、报表视图
10, 10, 300, 150, // 控件位置和大小
hwnd, (HMENU)1, // 父窗口句柄和控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 应用程序实例句柄
NULL);
// 添加列到列表视图
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_TEXT | LVCF_WIDTH; // 设置列属性:文本和宽度
lvColumn.cx = 100; // 列宽度
lvColumn.pszText = L"Column 1"; // 列标题
ListView_InsertColumn(hListView, 0, &lvColumn); // 插入第一列
lvColumn.pszText = L"Column 2"; // 列标题
ListView_InsertColumn(hListView, 1, &lvColumn); // 插入第二列
// 添加项到列表视图
LVITEM lvItem;
lvItem.mask = LVIF_TEXT; // 设置项属性:文本
lvItem.iItem = 0; // 第一行
lvItem.iSubItem = 0; // 第一列
lvItem.pszText = L"Item 1"; // 项文本
ListView_InsertItem(hListView, &lvItem); // 插入项
lvItem.iSubItem = 1; // 第二列
lvItem.pszText = L"SubItem 1"; // 子项文本
ListView_SetItem(hListView, &lvItem); // 设置子项文本
// 创建树形视图控件
HWND hTreeView = CreateWindow(
WC_TREEVIEW, // 树形视图的窗口类名
L"TreeView", // 控件初始文本
WS_VISIBLE | WS_CHILD | TVS_HASLINES | TVS_HASBUTTONS, // 控件风格:可见、子窗口、带线条和按钮
10, 170, 200, 150, // 控件位置和大小
hwnd, (HMENU)2, // 父窗口句柄和控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 应用程序实例句柄
NULL);
// 添加根节点
TVINSERTSTRUCT tvInsert;
tvInsert.hParent = NULL; // 根节点没有父节点
tvInsert.hInsertAfter = TVI_LAST; // 插入到末尾
tvInsert.item.mask = TVIF_TEXT; // 设置节点属性:文本
tvInsert.item.pszText = L"Root"; // 根节点文本
HTREEITEM hRoot = TreeView_InsertItem(hTreeView, &tvInsert); // 插入根节点
// 添加子节点
tvInsert.hParent = hRoot; // 子节点的父节点是根节点
tvInsert.item.pszText = L"Child"; // 子节点文本
TreeView_InsertItem(hTreeView, &tvInsert); // 插入子节点
// 创建选项卡控件
HWND hTabControl = CreateWindow(
WC_TABCONTROL, // 选项卡控件的窗口类名
L"TabControl", // 控件初始文本
WS_VISIBLE | WS_CHILD, // 控件风格:可见、子窗口
220, 170, 250, 150, // 控件位置和大小
hwnd, (HMENU)3, // 父窗口句柄和控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 应用程序实例句柄
NULL);
// 添加选项卡
TCITEM tcItem;
tcItem.mask = TCIF_TEXT; // 设置选项卡属性:文本
tcItem.pszText = L"Tab 1"; // 选项卡标题
TabCtrl_InsertItem(hTabControl, 0, &tcItem); // 插入第一个选项卡
tcItem.pszText = L"Tab 2"; // 选项卡标题
TabCtrl_InsertItem(hTabControl, 1, &tcItem); // 插入第二个选项卡
// 创建热键控件
HWND hHotKey = CreateWindow(
HOTKEY_CLASS, // 热键控件的窗口类名
NULL, // 控件初始文本
WS_VISIBLE | WS_CHILD, // 控件风格:可见、子窗口
10, 330, 200, 30, // 控件位置和大小
hwnd, (HMENU)4, // 父窗口句柄和控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 应用程序实例句柄
NULL);
// 创建按钮控件
HWND hButton = CreateWindow(
L"BUTTON", // 按钮控件的窗口类名
L"Click Me", // 按钮文本
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, // 控件风格:可见、子窗口、标准按钮
220, 330, 100, 30, // 控件位置和大小
hwnd, (HMENU)5, // 父窗口句柄和控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 应用程序实例句柄
NULL);
// 创建编辑框控件
HWND hEdit = CreateWindow(
L"EDIT", // 编辑框控件的窗口类名
L"Type here", // 初始文本
WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL | WS_BORDER, // 控件风格:可见、子窗口、自动水平滚动、带边框
330, 330, 150, 30, // 控件位置和大小
hwnd, (HMENU)6, // 父窗口句柄和控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 应用程序实例句柄
NULL);
break;
}
case WM_COMMAND: // 处理控件通知消息
{
int ctrlID = LOWORD(wParam); // 获取控件ID
int notifyCode = HIWORD(wParam); // 获取通知代码
// 处理按钮点击事件
if (ctrlID == 5 && notifyCode == BN_CLICKED)
{
MessageBox(hwnd, L"Button Clicked!", L"Notification", MB_OK); // 弹出消息框
}
break;
}
case WM_DESTROY: // 窗口销毁时触发
PostQuitMessage(0); // 退出消息循环
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam); // 默认消息处理
}
return 0;
}
// 主函数,程序入口
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// 初始化公共控件库
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX); // 结构体大小
icc.dwICC = ICC_LISTVIEW_CLASSES | ICC_TREEVIEW_CLASSES | ICC_TAB_CLASSES | ICC_HOTKEY_CLASS; // 需要初始化的控件类型
InitCommonControlsEx(&icc); // 初始化公共控件
// 注册窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc; // 窗口过程函数
wc.hInstance = hInstance; // 应用程序实例句柄
wc.lpszClassName = L"MyWindowClass"; // 窗口类名
if (!RegisterClass(&wc)) // 注册窗口类
{
MessageBox(NULL, L"Window Registration Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK); // 注册失败时弹出错误消息
return 0;
}
// 创建窗口
HWND hwnd = CreateWindow(
L"MyWindowClass", // 窗口类名
L"Common Controls Example", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口风格
CW_USEDEFAULT, CW_USEDEFAULT, 500, 450, // 窗口位置和大小
NULL, NULL, hInstance, NULL); // 父窗口句柄、菜单句柄、应用程序实例句柄、附加数据
if (hwnd == NULL) // 窗口创建失败
{
MessageBox(NULL, L"Window Creation Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK); // 弹出错误消息
return 0;
}
// 显示窗口
ShowWindow(hwnd, nCmdShow); // 显示窗口
UpdateWindow(hwnd); // 更新窗口
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) // 获取消息
{
TranslateMessage(&msg); // 翻译消息
DispatchMessage(&msg); // 分发消息
}
return msg.wParam; // 返回程序退出码
}
五、控件风格
每种控件除了通用的窗口风格(以 `WS_` 开头)外,还有自己的特定风格。以下是常见控件风格的前缀:
示例代码:
#include <windows.h>
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: {
// 创建按钮控件
CreateWindow(
"BUTTON", // 按钮类名
"Click Me", // 按钮文本
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, // 按钮风格
20, 20, 100, 30, // 位置和大小
hwnd, // 父窗口句柄
(HMENU)1, // 控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 实例句柄
NULL
);
// 创建编辑框控件
CreateWindow(
"EDIT", // 编辑框类名
"", // 初始文本
WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL, // 编辑框风格
20, 60, 200, 30, // 位置和大小
hwnd, // 父窗口句柄
(HMENU)2, // 控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 实例句柄
NULL
);
// 创建静态文本控件
CreateWindow(
"STATIC", // 静态文本类名
"Hello, World!", // 文本内容
WS_VISIBLE | WS_CHILD | SS_CENTER, // 静态文本风格
20, 100, 200, 30, // 位置和大小
hwnd, // 父窗口句柄
(HMENU)3, // 控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 实例句柄
NULL
);
// 创建组合框控件
CreateWindow(
"COMBOBOX", // 组合框类名
"", // 初始文本
WS_VISIBLE | WS_CHILD | CBS_DROPDOWN, // 组合框风格
20, 140, 200, 100, // 位置和大小
hwnd, // 父窗口句柄
(HMENU)4, // 控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 实例句柄
NULL
);
// 创建列表框控件
CreateWindow(
"LISTBOX", // 列表框类名
"", // 初始文本
WS_VISIBLE | WS_CHILD | LBS_NOTIFY | WS_BORDER, // 列表框风格
20, 180, 200, 100, // 位置和大小
hwnd, // 父窗口句柄
(HMENU)5, // 控件ID
((LPCREATESTRUCT)lParam)->hInstance, // 实例句柄
NULL
);
break;
}
case WM_COMMAND: {
// 处理控件事件
if (LOWORD(wParam) == 1) { // 按钮点击事件
MessageBox(hwnd, "Button Clicked!", "Info", MB_OK);
}
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// 主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 注册窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = "MyWindowClass";
RegisterClass(&wc);
// 创建窗口
HWND hwnd = CreateWindow(
"MyWindowClass", // 窗口类名
"Control Styles Example", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口风格
CW_USEDEFAULT, CW_USEDEFAULT, 300, 400, // 窗口位置和大小
NULL, NULL, hInstance, NULL
);
if (hwnd == NULL) {
return 0;
}
// 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
六、控件相关的消息
控件与父窗口的交互通过两种消息实现:
1. 控件控制消息:用于控制控件行为。
2. 控件通知消息:用于通知父窗口用户行为。
6.1 控件控制消息
每种控件除了接收通用窗口消息(以 `WM_` 开头)外,还有自己的专属消息。通过发送这些消息,可以控制控件的行为,而无需关心其内部处理逻辑。
6.2 控件通知消息
控件通知消息用于子控件向父窗口通知事件,例如子控件被点击或需要重绘。常见的控件通知消息分为两类:
- `WM_COMMAND`:标准控件的通知消息。
- `WM_NOTIFY`:通用控件的通知消息。
示例代码:
#include <windows.h>
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
{
// 获取控件ID和通知代码
int ctrlID = LOWORD(wParam);
int notifyCode = HIWORD(wParam);
// 处理按钮点击事件
if (ctrlID == 1 && notifyCode == BN_CLICKED)
{
MessageBox(hwnd, L"Button Clicked!", L"Notification", MB_OK);
}
break;
}
case WM_NOTIFY:
{
// 获取NMHDR结构体指针
NMHDR* pNmHdr = (NMHDR*)lParam;
// 处理来自通用控件的通知
if (pNmHdr->code == NM_CLICK)
{
MessageBox(hwnd, L"Control Clicked!", L"Notification", MB_OK);
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
// 主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// 注册窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"MyWindowClass";
if (!RegisterClass(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建窗口
HWND hwnd = CreateWindow(
L"MyWindowClass", L"My Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// 创建按钮控件
CreateWindow(
L"BUTTON", L"Click Me", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
50, 50, 100, 30, hwnd, (HMENU)1, hInstance, NULL);
// 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
本节介绍了 Windows 控件的基础知识,包括标准控件、通用控件、控件的创建、风格以及消息处理机制。通过示例代码,展示了如何创建按钮和编辑框,并处理控件通知消息。