原文: http://zetcode.com/gui/winapi/controlsIII/

在本章中,我们将结束有关 Windows API 控件的讨论。 我们将提到单选按钮,单选框,组合框和进度条。

RadioButton和分组框

在这里,我们介绍两个控件。分组框是围绕一组控件的矩形。 这些通常是单选按钮。 分组框具有描述控件的标签。 此控件的目的是对某种程度上相关的控件进行分组。单选按钮是一种特殊的按钮,用户可以选择但不能清除。 它允许用户从一组选项中选择一个独占选项。

radio_buttons.c

  1. #include <windows.h>
  2. #define ID_BLUE 1
  3. #define ID_YELLOW 2
  4. #define ID_ORANGE 3
  5. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  6. HINSTANCE g_hinst;
  7. COLORREF g_color;
  8. int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  9. PWSTR lpCmdLine, int nCmdShow) {
  10. HWND hwnd;
  11. MSG msg ;
  12. WNDCLASSW wc = {0};
  13. wc.lpszClassName = L"GroupBox";
  14. wc.hInstance = hInstance;
  15. wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  16. wc.lpfnWndProc = WndProc;
  17. wc.hCursor = LoadCursor(0, IDC_ARROW);
  18. g_hinst = hInstance;
  19. RegisterClassW(&wc);
  20. hwnd = CreateWindowW(wc.lpszClassName, L"GroupBox",
  21. WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  22. 100, 100, 300, 170, 0, 0, hInstance, 0);
  23. while (GetMessage(&msg, NULL, 0, 0)) {
  24. DispatchMessage(&msg);
  25. }
  26. return (int) msg.wParam;
  27. }
  28. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
  29. WPARAM wParam, LPARAM lParam) {
  30. HDC hdc;
  31. PAINTSTRUCT ps;
  32. HBRUSH hBrush, holdBrush;
  33. HPEN hPen, holdPen;
  34. switch(msg) {
  35. case WM_CREATE:
  36. CreateWindowW(L"Button", L"Choose colour",
  37. WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
  38. 10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL);
  39. CreateWindowW(L"Button", L"Blue",
  40. WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
  41. 20, 30, 100, 30, hwnd, (HMENU) ID_BLUE , g_hinst, NULL);
  42. CreateWindowW(L"Button", L"Yellow",
  43. WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
  44. 20, 55, 100, 30, hwnd, (HMENU) ID_YELLOW , g_hinst, NULL);
  45. CreateWindowW(L"Button", L"Orange",
  46. WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
  47. 20, 80, 100, 30, hwnd, (HMENU) ID_ORANGE , g_hinst, NULL);
  48. break;
  49. case WM_COMMAND:
  50. if (HIWORD(wParam) == BN_CLICKED) {
  51. switch (LOWORD(wParam)) {
  52. case ID_BLUE:
  53. g_color = RGB(0, 76, 255);
  54. break;
  55. case ID_YELLOW:
  56. g_color = RGB(255, 255, 0);
  57. break;
  58. case ID_ORANGE:
  59. g_color = RGB(255, 123, 0);
  60. break;
  61. }
  62. InvalidateRect(hwnd, NULL, TRUE);
  63. }
  64. break;
  65. case WM_PAINT:
  66. hdc = BeginPaint(hwnd, &ps);
  67. hBrush = CreateSolidBrush(g_color);
  68. hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
  69. holdPen = SelectObject(hdc, hPen);
  70. holdBrush = (HBRUSH) SelectObject(hdc, hBrush);
  71. Rectangle(hdc, 160, 20, 260, 120);
  72. SelectObject(hdc, holdBrush);
  73. SelectObject(hdc, holdPen);
  74. DeleteObject(hPen);
  75. DeleteObject(hBrush);
  76. EndPaint(hwnd, &ps);
  77. break;
  78. case WM_DESTROY:
  79. PostQuitMessage(0);
  80. break;
  81. }
  82. return DefWindowProcW(hwnd, msg, wParam, lParam);
  83. }

在我们的示例中,我们有一个带有三个单选按钮的分组框。 通过单击单选按钮,我们为右侧的矩形选择背景色。

  1. CreateWindowW(L"Button", L"Choose colour",
  2. WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
  3. 10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL);

分组框是一种特殊的按BS_GROUPBOX样式创建的按钮。

  1. CreateWindowW(L"Button", L"Blue",
  2. WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
  3. 20, 30, 100, 30, hwnd, (HMENU) ID_BLUE , g_hinst, NULL);

单选按钮也是BS_AUTORADIOBUTTON样式的特殊按钮。

  1. case ID_BLUE:
  2. g_color = RGB(0, 76, 255);
  3. break;

如果单击单选按钮,则将使用选定的颜色填充全局变量。 此变量用于创建填充矩形的画笔。

  1. InvalidateRect(hwnd, NULL, TRUE);

我们使矩形(在本例中为整个窗口)无效,这将导致重绘客户区。 这将启动WM_PAINT消息。 在WM_PAINT消息期间,我们绘制矩形。 在 GDI 一章中更详细地说明了绘图。

Windows API 控件 III - 图1

图:GroupBox中的单选按钮

ComboBox

组合框是编辑框或静态文本与列表的组合。 当我们需要从可用选项列表中选择一个项目时,将使用一个组合框。

combobox.c

  1. #include <windows.h>
  2. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  3. HINSTANCE g_hinst;
  4. int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  5. PWSTR lpCmdLine, int nCmdShow) {
  6. HWND hwnd;
  7. MSG msg ;
  8. WNDCLASSW wc = {0};
  9. wc.lpszClassName = L"Application";
  10. wc.hInstance = hInstance ;
  11. wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  12. wc.lpfnWndProc = WndProc ;
  13. wc.hCursor = LoadCursor(0,IDC_ARROW);
  14. g_hinst = hInstance;
  15. RegisterClassW(&wc);
  16. hwnd = CreateWindowW(wc.lpszClassName, L"Combo box",
  17. WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  18. 100, 100, 270, 170, 0, 0, hInstance, 0);
  19. while (GetMessage(&msg, NULL, 0, 0)) {
  20. DispatchMessage(&msg);
  21. }
  22. return (int) msg.wParam;
  23. }
  24. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
  25. WPARAM wParam, LPARAM lParam) {
  26. static HWND hwndCombo, hwndStatic;
  27. const wchar_t *items[] = { L"FreeBSD", L"OpenBSD",
  28. L"NetBSD", L"Solaris", L"Arch" };
  29. switch(msg) {
  30. case WM_CREATE:
  31. hwndCombo = CreateWindowW(L"Combobox", NULL,
  32. WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
  33. 10, 10, 120, 110, hwnd, NULL, g_hinst, NULL);
  34. CreateWindowW(L"Button", L"Drop down",
  35. WS_CHILD | WS_VISIBLE,
  36. 150, 10, 90, 25, hwnd, (HMENU) 1, g_hinst, NULL);
  37. hwndStatic = CreateWindowW(L"Static", L"",
  38. WS_CHILD | WS_VISIBLE,
  39. 150, 80, 90, 25, hwnd, NULL, g_hinst, NULL);
  40. for (int i = 0; i < 4; i++ ) {
  41. SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
  42. }
  43. break;
  44. case WM_COMMAND:
  45. if (HIWORD(wParam) == BN_CLICKED) {
  46. SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0);
  47. }
  48. if (HIWORD(wParam) == CBN_SELCHANGE) {
  49. LRESULT sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
  50. SetWindowTextW(hwndStatic, items[sel]);
  51. }
  52. break;
  53. case WM_DESTROY:
  54. PostQuitMessage(0);
  55. break;
  56. }
  57. return DefWindowProcW(hwnd, msg, wParam, lParam);
  58. }

在我们的示例中,我们在窗口上放置了三个控件:一个组合框,一个按钮和一个静态文本。 静态文本显示组合框中当前选择的项目。 用于演示CBN_SELCHANGE组合框消息。 该按钮以编程方式打开组合框。

  1. hwndCombo = CreateWindowW(L"Combobox", NULL,
  2. WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
  3. 10, 10, 120, 110, hwnd, NULL, g_hinst, NULL);

要创建一个组合框,我们使用L"Combobox"窗口类。 CBS_DROPDOWN标志创建一个下拉列表。

  1. for (int i = 0; i < 4; i++ ) {
  2. SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
  3. }

我们用项目填充组合框。 要向组合框添加字符串,我们发送CB_ADDSTRING消息。

  1. if (HIWORD(wParam) == BN_CLICKED) {
  2. SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0);
  3. }

单击该按钮将导致发送CB_SHOWDROPDOWN消息,该消息将以编程方式调用下拉框。

如果从组合框中选择一个项目,则窗口过程将接收WM_COMMAND消息,并在wParam参数的高位字中带有通知消息CBN_SELCHANGE

  1. if (HIWORD(wParam) == CBN_SELCHANGE) {
  2. LRESULT sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
  3. SetWindowTextW(hwndStatic, items[sel]);
  4. }

我们找出当前选择的项目。 我们向组合框发送CB_GETCURSEL消息。 该函数返回当前所选项目的索引。 我们将静态文本设置为当前选择的字符串。

Windows API 控件 III - 图2

图:组合框

进度条

进度条是当我们处理冗长的任务时使用的控件。 它具有动画效果,以便用户知道我们的任务正在进行中。

progressbar.c

  1. #include <windows.h>
  2. #include <commctrl.h>
  3. #define ID_BUTTON 1
  4. #define ID_TIMER 2
  5. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  6. void CreateControls(HWND);
  7. HWND hwndPrgBar;
  8. HWND hbtn;
  9. int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  10. PWSTR lpCmdLine, int nCmdShow) {
  11. HWND hwnd;
  12. MSG msg ;
  13. WNDCLASSW wc = {0};
  14. wc.lpszClassName = L"Application";
  15. wc.hInstance = hInstance;
  16. wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  17. wc.lpfnWndProc = WndProc;
  18. wc.hCursor = LoadCursor(0, IDC_ARROW);
  19. RegisterClassW(&wc);
  20. hwnd = CreateWindowW(wc.lpszClassName, L"Progress bar",
  21. WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  22. 100, 100, 260, 170, 0, 0, hInstance, 0);
  23. while (GetMessage(&msg, NULL, 0, 0)) {
  24. DispatchMessage(&msg);
  25. }
  26. return (int) msg.wParam;
  27. }
  28. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
  29. WPARAM wParam, LPARAM lParam) {
  30. static int i = 0;
  31. switch(msg) {
  32. case WM_CREATE:
  33. CreateControls(hwnd);
  34. break;
  35. case WM_TIMER:
  36. SendMessage(hwndPrgBar, PBM_STEPIT, 0, 0);
  37. i++;
  38. if (i == 150) {
  39. KillTimer(hwnd, ID_TIMER);
  40. SendMessageW(hbtn, WM_SETTEXT, (WPARAM) NULL, (LPARAM) L"Start");
  41. i = 0;
  42. }
  43. break;
  44. case WM_COMMAND:
  45. if (i == 0) {
  46. i = 1;
  47. SendMessage(hwndPrgBar, PBM_SETPOS, 0, 0);
  48. SetTimer(hwnd, ID_TIMER, 5, NULL);
  49. SendMessageW(hbtn, WM_SETTEXT, (WPARAM) NULL, (LPARAM) L"In progress");
  50. }
  51. break;
  52. case WM_DESTROY:
  53. KillTimer(hwnd, ID_TIMER);
  54. PostQuitMessage(0);
  55. break;
  56. }
  57. return DefWindowProcW(hwnd, msg, wParam, lParam);
  58. }
  59. void CreateControls(HWND hwnd) {
  60. INITCOMMONCONTROLSEX icex;
  61. icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  62. icex.dwICC = ICC_PROGRESS_CLASS;
  63. InitCommonControlsEx(&icex);
  64. hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL,
  65. WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
  66. 30, 20, 190, 25, hwnd, NULL, NULL, NULL);
  67. hbtn = CreateWindowW(L"Button", L"Start",
  68. WS_CHILD | WS_VISIBLE,
  69. 85, 90, 85, 25, hwnd, (HMENU) 1, NULL, NULL);
  70. SendMessage(hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150));
  71. SendMessage(hwndPrgBar, PBM_SETSTEP, 1, 0);
  72. }

在我们的示例中,我们有一个进度条和一个按钮。 该按钮启动一个计时器,该计时器更新进度条。

  1. hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL,
  2. WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
  3. 30, 20, 190, 25, hwnd, NULL, NULL, NULL);

我们使用PROGRESS_CLASS类名称和PBS_SMOOTH样式创建进度条控件。

  1. SendMessage(hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150));
  2. SendMessage(hwndPrgBar, PBM_SETSTEP, 1, 0);

我们设置进度条的范围及其步骤。

  1. i = 1;
  2. SendMessage(hwndPrgBar, PBM_SETPOS, 0, 0);
  3. SetTimer(hwnd, ID_TIMER, 5, NULL);

当按下开始按钮时,我们将i值设置为 1,设置进度条的初始位置,然后启动计时器。 计时器将定期向窗口过程发送WM_TIMER消息,直到被杀死。

  1. SendMessageW(hbtn, WM_SETTEXT, (WPARAM) NULL, (LPARAM) L"In progress");

当计时器进行时,我们更改按钮的标签。

  1. case WM_TIMER:
  2. SendMessage(hwndPrgBar, PBM_STEPIT, 0, 0);
  3. i++;
  4. if (i == 150) {
  5. KillTimer(hwnd, ID_TIMER);
  6. SendMessageW(hbtn, WM_SETTEXT, (WPARAM) NULL, (LPARAM) L"Start");
  7. i = 0;
  8. }
  9. break;

当我们收到WM_TIMER消息时,我们通过发送PBM_STEPIT消息的一步来更新进度条。 当i变量达到进度条的上限时,计时器将被终止。

Windows API 控件 III - 图3

图:进度条

在 Windows API 教程的这一部分中,我们已经完成了 Windows 控件的介绍。