返回介绍

Windows API controls III

发布于 2025-02-22 22:19:24 字数 13473 浏览 0 评论 0 收藏 0

In this chapter, we will finish talking about Windows API controls. We will mention radio buttons, radio box, combo box, and a progress bar.

Radio buttons and GroupBox

Here we introduce two controls. A group box is a rectangle that surrounds a set of controls. These are often radio buttons. A group box has a label that describes the control. The purpose of this control is to group controls that are somehow related. A radio button is a special kind of button that can be selected by the user, but not cleared. It allows the user to select a single exclusive choice from a group of options.

radio_buttons.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);
}

In our example, we have a group box with three radio buttons. By clicking on the radio button, we select a background colour for the rectangle on the right.

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

A group box is a special kind of a button created with the BS_GROUPBOX style.

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

A radio button is also a special kind of a button with BS_AUTORADIOBUTTON style.

case ID_BLUE:
  g_color = RGB(0, 76, 255);
  break;

If we click on the radio button, a global variable is filled with selected colour. This variable is used to create a brush that fills the rectangle.

InvalidateRect(hwnd, NULL, TRUE);

We invalidate the rectangle (in this case whole window), which will cause the client area to be redrawn. This will launch a WM_PAINT message. During the WM_PAINT message, we draw the rectangle. Drawing is explained in GDI chapter in more detail.

Radio buttons in a GroupBox
Figure: Radio buttons in a GroupBox

Combo box

A combo box is a combination of an edit box or static text and a list. A combo box is used when we need to select an item from a list of available options.

combobox.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);
}

In our example, we put three controls on the window: a combo box, a button, and a static text. The static text displays the currently selected item from the combo box. It is used to demonstrate the CBN_SELCHANGE combo box message. The button programatically opens the combo box.

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

To create a combo box, we use the L"Combobox" window class. The CBS_DROPDOWN flag creates a drop-down list.

for (int i = 0; i < 4; i++ ) {

  SendMessageW(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
}

We fill the combo box with items. To add a string to the combo box, we send a CB_ADDSTRING message.

if (HIWORD(wParam) == BN_CLICKED) {

  SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0);
}

Clicking on the button causes a CB_SHOWDROPDOWN message to be send, which programmatically invokes a drop down of the combo box.

If we select an item from the combo box, the window procedure receives the WM_COMMAND message with the notification message CBN_SELCHANGE in the high-order word of the wParam parameter.

if (HIWORD(wParam) == CBN_SELCHANGE) {    

  LRESULT sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
  SetWindowTextW(hwndStatic, items[sel]);
}

We figure out the currently selected item. We send a CB_GETCURSEL message to the combo box. The function returns the index of the currently selected item. We set the static text to the currently selected string.

Combo box
Figure: Combo box

Progress bar

A progress bar is a control that is used when we process lengthy tasks. It is animated so that the user knows that our task is progressing.

progressbar.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);
}

In our example, we have a progress bar and a button. The button starts a timer which updates the progress bar.

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

We create a progress bar control with PROGRESS_CLASS class name and PBS_SMOOTH style.

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

We set the range of the progress bar and its step.

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

When we press the Start button, we set the i value to 1, set the initial position of the progress bar, and start the timer. The timer will periodically send a WM_TIMER message to the window procedure, until it is killed.

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

When the timer is in progress, we change the label of the button.

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;

When we receive the WM_TIMER message, we update the progress bar by one step sending the PBM_STEPIT message. The timer is killed when the i variable reaches the upper limit of the progress bar.

Progress bar
Figure: Progress bar

In this part of the Windows API tutorial, we have finished covering Windows controls.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文