ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# Windows API 控件 III > 原文: [http://zetcode.com/gui/winapi/controlsIII/](http://zetcode.com/gui/winapi/controlsIII/) 在本章中,我们将结束有关 Windows API 控件的讨论。 我们将提到单选按钮,单选框,组合框和进度条。 ## `RadioButton`和分组框 在这里,我们介绍两个控件。分组框是围绕一组控件的矩形。 这些通常是单选按钮。 分组框具有描述控件的标签。 此控件的目的是对某种程度上相关的控件进行分组。单选按钮是一种特殊的按钮,用户可以选择但不能清除。 它允许用户从一组选项中选择一个独占选项。 `radio_buttons.c` ```c #include <windows.h> #define ID_BLUE 1 #define ID_YELLOW 2 #define ID_ORANGE 3 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); HINSTANCE g_hinst; COLORREF g_color; int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG msg ; WNDCLASSW wc = {0}; wc.lpszClassName = L"GroupBox"; wc.hInstance = hInstance; wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE); wc.lpfnWndProc = WndProc; wc.hCursor = LoadCursor(0, IDC_ARROW); g_hinst = hInstance; RegisterClassW(&wc); hwnd = CreateWindowW(wc.lpszClassName, L"GroupBox", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 300, 170, 0, 0, hInstance, 0); while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } return (int) msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; HBRUSH hBrush, holdBrush; HPEN hPen, holdPen; switch(msg) { case WM_CREATE: CreateWindowW(L"Button", L"Choose colour", WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL); CreateWindowW(L"Button", L"Blue", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 20, 30, 100, 30, hwnd, (HMENU) ID_BLUE , g_hinst, NULL); CreateWindowW(L"Button", L"Yellow", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 20, 55, 100, 30, hwnd, (HMENU) ID_YELLOW , g_hinst, NULL); CreateWindowW(L"Button", L"Orange", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 20, 80, 100, 30, hwnd, (HMENU) ID_ORANGE , g_hinst, NULL); break; case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED) { switch (LOWORD(wParam)) { case ID_BLUE: g_color = RGB(0, 76, 255); break; case ID_YELLOW: g_color = RGB(255, 255, 0); break; case ID_ORANGE: g_color = RGB(255, 123, 0); break; } InvalidateRect(hwnd, NULL, TRUE); } break; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); hBrush = CreateSolidBrush(g_color); hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0)); holdPen = SelectObject(hdc, hPen); holdBrush = (HBRUSH) SelectObject(hdc, hBrush); Rectangle(hdc, 160, 20, 260, 120); SelectObject(hdc, holdBrush); SelectObject(hdc, holdPen); DeleteObject(hPen); DeleteObject(hBrush); EndPaint(hwnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProcW(hwnd, msg, wParam, lParam); } ``` 在我们的示例中,我们有一个带有三个单选按钮的分组框。 通过单击单选按钮,我们为右侧的矩形选择背景色。 ```c CreateWindowW(L"Button", L"Choose colour", WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL); ``` 分组框是一种特殊的按`BS_GROUPBOX`样式创建的按钮。 ```c CreateWindowW(L"Button", L"Blue", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 20, 30, 100, 30, hwnd, (HMENU) ID_BLUE , g_hinst, NULL); ``` 单选按钮也是`BS_AUTORADIOBUTTON`样式的特殊按钮。 ```c case ID_BLUE: g_color = RGB(0, 76, 255); break; ``` 如果单击单选按钮,则将使用选定的颜色填充全局变量。 此变量用于创建填充矩形的画笔。 ```c InvalidateRect(hwnd, NULL, TRUE); ``` 我们使矩形(在本例中为整个窗口)无效,这将导致重绘客户区。 这将启动`WM_PAINT`消息。 在`WM_PAINT`消息期间,我们绘制矩形。 在 [GDI](../gdi) 一章中更详细地说明了绘图。 ![Radio buttons in a GroupBox](https://img.kancloud.cn/9f/cf/9fcfa2bf07db3613a62e1021be6e48b2_300x170.jpg) 图:`GroupBox`中的单选按钮 ## `ComboBox` 组合框是编辑框或静态文本与列表的组合。 当我们需要从可用选项列表中选择一个项目时,将使用一个组合框。 `combobox.c` ```c #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); HINSTANCE g_hinst; int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG msg ; WNDCLASSW wc = {0}; wc.lpszClassName = L"Application"; wc.hInstance = hInstance ; wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE); wc.lpfnWndProc = WndProc ; wc.hCursor = LoadCursor(0,IDC_ARROW); g_hinst = hInstance; RegisterClassW(&wc); hwnd = CreateWindowW(wc.lpszClassName, L"Combo box", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 270, 170, 0, 0, hInstance, 0); while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } return (int) msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND hwndCombo, hwndStatic; const wchar_t *items[] = { L"FreeBSD", L"OpenBSD", L"NetBSD", L"Solaris", L"Arch" }; switch(msg) { case WM_CREATE: hwndCombo = CreateWindowW(L"Combobox", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, 10, 10, 120, 110, hwnd, NULL, g_hinst, NULL); CreateWindowW(L"Button", L"Drop down", WS_CHILD | WS_VISIBLE, 150, 10, 90, 25, hwnd, (HMENU) 1, g_hinst, NULL); hwndStatic = CreateWindowW(L"Static", L"", WS_CHILD | WS_VISIBLE, 150, 80, 90, 25, hwnd, NULL, g_hinst, NULL); for (int i = 0; i < 4; i++ ) { SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]); } break; case WM_COMMAND: if (HIWORD(wParam) == BN_CLICKED) { SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0); } if (HIWORD(wParam) == CBN_SELCHANGE) { LRESULT sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0); SetWindowTextW(hwndStatic, items[sel]); } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProcW(hwnd, msg, wParam, lParam); } ``` 在我们的示例中,我们在窗口上放置了三个控件:一个组合框,一个按钮和一个静态文本。 静态文本显示组合框中当前选择的项目。 用于演示`CBN_SELCHANGE`组合框消息。 该按钮以编程方式打开组合框。 ```c hwndCombo = CreateWindowW(L"Combobox", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, 10, 10, 120, 110, hwnd, NULL, g_hinst, NULL); ``` 要创建一个组合框,我们使用`L"Combobox"`窗口类。 `CBS_DROPDOWN`标志创建一个下拉列表。 ```c for (int i = 0; i < 4; i++ ) { SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]); } ``` 我们用项目填充组合框。 要向组合框添加字符串,我们发送`CB_ADDSTRING`消息。 ```c if (HIWORD(wParam) == BN_CLICKED) { SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0); } ``` 单击该按钮将导致发送`CB_SHOWDROPDOWN`消息,该消息将以编程方式调用下拉框。 如果从组合框中选择一个项目,则窗口过程将接收`WM_COMMAND`消息,并在`wParam`参数的高位字中带有通知消息`CBN_SELCHANGE`。 ```c if (HIWORD(wParam) == CBN_SELCHANGE) { LRESULT sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0); SetWindowTextW(hwndStatic, items[sel]); } ``` 我们找出当前选择的项目。 我们向组合框发送`CB_GETCURSEL`消息。 该函数返回当前所选项目的索引。 我们将静态文本设置为当前选择的字符串。 ![Combo box](https://img.kancloud.cn/70/93/70937684dd7372ab89944a33b93eaf9c_270x170.jpg) 图:组合框 ## 进度条 进度条是当我们处理冗长的任务时使用的控件。 它具有动画效果,以便用户知道我们的任务正在进行中。 `progressbar.c` ```c #include <windows.h> #include <commctrl.h> #define ID_BUTTON 1 #define ID_TIMER 2 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void CreateControls(HWND); HWND hwndPrgBar; HWND hbtn; int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG msg ; WNDCLASSW wc = {0}; wc.lpszClassName = L"Application"; wc.hInstance = hInstance; wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE); wc.lpfnWndProc = WndProc; wc.hCursor = LoadCursor(0, IDC_ARROW); RegisterClassW(&wc); hwnd = CreateWindowW(wc.lpszClassName, L"Progress bar", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 260, 170, 0, 0, hInstance, 0); while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } return (int) msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static int i = 0; switch(msg) { case WM_CREATE: CreateControls(hwnd); break; case WM_TIMER: SendMessage(hwndPrgBar, PBM_STEPIT, 0, 0); i++; if (i == 150) { KillTimer(hwnd, ID_TIMER); SendMessageW(hbtn, WM_SETTEXT, (WPARAM) NULL, (LPARAM) L"Start"); i = 0; } break; case WM_COMMAND: if (i == 0) { i = 1; SendMessage(hwndPrgBar, PBM_SETPOS, 0, 0); SetTimer(hwnd, ID_TIMER, 5, NULL); SendMessageW(hbtn, WM_SETTEXT, (WPARAM) NULL, (LPARAM) L"In progress"); } break; case WM_DESTROY: KillTimer(hwnd, ID_TIMER); PostQuitMessage(0); break; } return DefWindowProcW(hwnd, msg, wParam, lParam); } void CreateControls(HWND hwnd) { INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); icex.dwICC = ICC_PROGRESS_CLASS; InitCommonControlsEx(&icex); hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE | PBS_SMOOTH, 30, 20, 190, 25, hwnd, NULL, NULL, NULL); hbtn = CreateWindowW(L"Button", L"Start", WS_CHILD | WS_VISIBLE, 85, 90, 85, 25, hwnd, (HMENU) 1, NULL, NULL); SendMessage(hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150)); SendMessage(hwndPrgBar, PBM_SETSTEP, 1, 0); } ``` 在我们的示例中,我们有一个进度条和一个按钮。 该按钮启动一个计时器,该计时器更新进度条。 ```c hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE | PBS_SMOOTH, 30, 20, 190, 25, hwnd, NULL, NULL, NULL); ``` 我们使用`PROGRESS_CLASS`类名称和`PBS_SMOOTH`样式创建进度条控件。 ```c SendMessage(hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150)); SendMessage(hwndPrgBar, PBM_SETSTEP, 1, 0); ``` 我们设置进度条的范围及其步骤。 ```c i = 1; SendMessage(hwndPrgBar, PBM_SETPOS, 0, 0); SetTimer(hwnd, ID_TIMER, 5, NULL); ``` 当按下开始按钮时,我们将`i`值设置为 1,设置进度条的初始位置,然后启动计时器。 计时器将定期向窗口过程发送`WM_TIMER`消息,直到被杀死。 ```c SendMessageW(hbtn, WM_SETTEXT, (WPARAM) NULL, (LPARAM) L"In progress"); ``` 当计时器进行时,我们更改按钮的标签。 ```c case WM_TIMER: SendMessage(hwndPrgBar, PBM_STEPIT, 0, 0); i++; if (i == 150) { KillTimer(hwnd, ID_TIMER); SendMessageW(hbtn, WM_SETTEXT, (WPARAM) NULL, (LPARAM) L"Start"); i = 0; } break; ``` 当我们收到`WM_TIMER`消息时,我们通过发送`PBM_STEPIT`消息的一步来更新进度条。 当`i`变量达到进度条的上限时,计时器将被终止。 ![Progress bar](https://img.kancloud.cn/32/ad/32add02fadd8337ec371c01fb7f97137_260x170.jpg) 图:进度条 在 Windows API 教程的这一部分中,我们已经完成了 Windows 控件的介绍。