Home  Contents

Controls II.

We continue with Windows Controls. We will show how to use a Trackbar, a Tooltip and a Month calendar control.

Trackbar

A trackbar is a window that contains a slider and optional tick marks. We move the slider using the mouse or keyboard. A trackbar is used to select discrete values from a range of consecutive values. This control is called a slider on other platforms.

#include <windows.h>
#include <commctrl.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateControls(HWND hwnd);
void UpdateLabel(void);

HWND hTrack;
HWND hlbl;

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
  PWSTR lpCmdLine, int nCmdShow)
{
  HWND hwnd;
  MSG  msg ;

  WNDCLASSW wc = {0};
  wc.lpszClassName = L"Trackbar";
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClassW(&wc);
  hwnd = CreateWindowW(wc.lpszClassName, L"Trackbar", 
    WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 350, 180, 0, 0, hInstance, 0);
  
  while( GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, 
  WPARAM wParam, LPARAM lParam )
{
  switch(msg)  
  {
     case WM_CREATE:
       CreateControls(hwnd);
       break;

     case WM_HSCROLL:
       UpdateLabel();
       break;

     case WM_DESTROY:
       PostQuitMessage(0);
       break; 
  }

  return DefWindowProcW(hwnd, msg, wParam, lParam);
}

void CreateControls(HWND hwnd)
{
  HWND hLeftLabel = CreateWindowW(L"STATIC", L"0", 
    WS_CHILD | WS_VISIBLE, 0, 0, 10, 30, hwnd, (HMENU)1, NULL, NULL);

  HWND hRightLabel = CreateWindowW(L"STATIC", L"100", 
    WS_CHILD | WS_VISIBLE, 0, 0, 30, 30, hwnd, (HMENU)2, NULL, NULL);

  hlbl = CreateWindowW(L"STATIC", L"0", WS_CHILD | WS_VISIBLE, 
    270, 20, 30, 30, hwnd, (HMENU)3, NULL, NULL);

  INITCOMMONCONTROLSEX icex;

  icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  icex.dwICC  = ICC_LISTVIEW_CLASSES;
  InitCommonControlsEx(&icex); 

  hTrack = CreateWindowW(L"msctls_trackbar32", L"Trackbar Control",
      WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS,
      20, 20, 170, 30, hwnd, (HMENU) 3, NULL, NULL);

  SendMessageW(hTrack, TBM_SETRANGE,  TRUE, MAKELONG(0, 100)); 
  SendMessageW(hTrack, TBM_SETPAGESIZE, 0,  10); 
  SendMessageW(hTrack, TBM_SETTICFREQ, 10, 0); 
  SendMessageW(hTrack, TBM_SETPOS, FALSE, 0); 
  SendMessageW(hTrack, TBM_SETBUDDY, TRUE, (LPARAM) hLeftLabel); 
  SendMessageW(hTrack, TBM_SETBUDDY, FALSE, (LPARAM) hRightLabel); 
}

void UpdateLabel(void)
{
  LRESULT pos = SendMessageW(hTrack, TBM_GETPOS, 0, 0);
  wchar_t buf[4];
  wsprintfW(buf, L"%ld", pos);

  SetWindowTextW(hlbl, buf);
}

In our example we display a Trackbar control with three static text controls. Two of them are attached to the left and to the right of the trackbar. They are called buddies. By dragging the slider, we change the text of the third static control.

HWND hLeftLabel = CreateWindowW(L"STATIC", L"0", 
  WS_CHILD | WS_VISIBLE, 0, 0, 10, 30, hwnd, (HMENU)1, NULL, NULL);

HWND hRightLabel = CreateWindowW(L"STATIC", L"100", 
  WS_CHILD | WS_VISIBLE, 0, 0, 30, 30, hwnd, (HMENU)2, NULL, NULL);

hlbl = CreateWindowW(L"STATIC", L"0", WS_CHILD | WS_VISIBLE, 
  270, 20, 30, 30, hwnd, (HMENU)3, NULL, NULL);

Three static controls are created. Two controls will display the minimum and maximum value of the Trackbar control. The last one will display the currently selected value.

INITCOMMONCONTROLSEX icex;

icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC  = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&icex); 

If we want to use one of the common controls, we need to load the common control DLL (comctl32.dll) and register specific common control classes from the DLL. The InitCommonControlsEx() must call this function before creating a common control.

hTrack = CreateWindowW(L"msctls_trackbar32", L"Trackbar Control",
    WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS,
    20, 20, 170, 30, hwnd, (HMENU) 3, NULL, NULL);

A Trackbar common control is created. The TBS_AUTOTICKS style creates a tick mark for each increment in its range of values.

SendMessageW(hTrack, TBM_SETRANGE,  TRUE, MAKELONG(0, 100)); 
SendMessageW(hTrack, TBM_SETPAGESIZE, 0,  10); 
SendMessageW(hTrack, TBM_SETTICFREQ, 10, 0); 
SendMessageW(hTrack, TBM_SETPOS, FALSE, 0); 

We are not yet finished with the Trackbar control. We send four messages to the control. We send a TBM_SETRANGE to set the trackbar range. To set the page size, we send the TBM_SETPAGESIZE message. To set the tick frequency, we send the TBM_SETTICFREQ message. To set the current slider position we send the TBM_SETPOS.

SendMessageW(hTrack, TBM_SETBUDDY, TRUE, (LPARAM) hLeftLabel); 
SendMessageW(hTrack, TBM_SETBUDDY, FALSE, (LPARAM) hRightLabel);  

We set the Trackbar buddies by sending the TBM_SETBUDDY message. The third parameter will decide, whether the buddy is located to the left (TRUE) or to the right (FALSE) of the control.

case WM_HSCROLL:
  UpdateLabel();
  break;

When we move the Trackbar slider, the window procedure receives the WM_HSCROLL message. (In case of a horizontal trackbar.)

void UpdateLabel(void)
{
  LRESULT pos = SendMessageW(hTrack, TBM_GETPOS, 0, 0);
  wchar_t buf[4];
  wsprintfW(buf, L"%ld", pos);

  SetWindowTextW(hlbl, buf);
}

In the UpdateLabel() method, we we get the current slider position by sending the TMB_GETPOS message. The received value is converted to text using the wsprintfW() function. Finally, the text of the static control is set with the SetWindowTextW() function.

Trackbar
Figure: Trackbar

A tooltip

A tooltip is a common graphical user element. Tooltip is hidden most of the time. It is a small box that appears near an GUI object when a mouse pointer passes over it. It displays a brief message explaining the object. Tooltips are part of the help system of an application.

#include <windows.h>
#include <commctrl.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateMyTooltip(HWND);


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
			LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = "Tooltip" ;
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0, IDC_ARROW);
  
  RegisterClass(&wc);
  CreateWindow( wc.lpszClassName, "Tooltip",
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 200, 150, 0, 0, hInstance, 0);  

  while( GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
  switch(msg)  
  {
    case WM_CREATE:
        CreateMyTooltip(hwnd);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

void CreateMyTooltip (HWND hwnd)
{

    INITCOMMONCONTROLSEX iccex; 
    HWND hwndTT;                

    TOOLINFO ti;
    char tooltip[30] = "A main window";
    RECT rect;                 
  
    iccex.dwICC = ICC_WIN95_CLASSES;
    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    InitCommonControlsEx(&iccex);

    hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
        WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,		
        0, 0, 0, 0, hwnd, NULL, NULL, NULL );

    SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0,
        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
   
    GetClientRect (hwnd, &rect);

    ti.cbSize = sizeof(TOOLINFO);
    ti.uFlags = TTF_SUBCLASS;
    ti.hwnd = hwnd;
    ti.hinst = NULL;
    ti.uId = 0;
    ti.lpszText = tooltip;
    ti.rect.left = rect.left;    
    ti.rect.top = rect.top;
    ti.rect.right = rect.right;
    ti.rect.bottom = rect.bottom;

    SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);	
} 		

In our example, we set a tooltip for the main window.

 INITCOMMONCONTROLSEX iccex;
 ...
 iccex.dwICC = ICC_WIN95_CLASSES;
 iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
 InitCommonControlsEx(&iccex);

A tooltip is a part of common controls. We must initialize common controls.

Creation of a tooltip consists of several steps. We must create a tooltip window. Then we make it a topmost window, so that it is not covered by another window. We create a tooltip text and TOOLTIPINFO structure. The structure must be filled with important info. The window handle, tooltip text and the rectangle, which will our tooltip cover. In our example, our tooltip will cover the whole client area of a window.

 SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);

The tooltip is really added to the window, after we send the TTM_ADDTOOL message.

Tooltip

Month Calendar Control

A Month Calendar is a complex control which is used to select a a date. We can select a date in a simple and intuitive way.

#include <windows.h>
#include <commctrl.h>
#include <wchar.h>

#define DATE_SIZE 20

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateControls(HWND);
void GetSelectedDate(HWND, HWND);

HWND hlbl;
HWND hMonthCal;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
  LPSTR lpCmdLine, int nCmdShow)
{
  HWND hwnd;
  MSG  msg;
    
  WNDCLASSW wc = {0};
  wc.lpszClassName = L"Month Calendar";
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClassW(&wc);

  hwnd = CreateWindowW(wc.lpszClassName, L"Month Calendar",
    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    100, 100, 250, 300, 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 )
{
  LPNMHDR lpNmHdr;

  switch(msg)  
  {
    case WM_CREATE:

      CreateControls(hwnd);
      break;

    case WM_NOTIFY:

      lpNmHdr = (LPNMHDR) lParam;

      if (lpNmHdr->code==MCN_SELECT) {
        GetSelectedDate(hMonthCal, hlbl);
      }
      break;

    case WM_DESTROY:

      PostQuitMessage(0);
      break; 
  }

  return DefWindowProcW(hwnd, msg, wParam, lParam);
}

void CreateControls(HWND hwnd)
{
  hlbl = CreateWindowW(L"STATIC", L"", 
    WS_CHILD | WS_VISIBLE, 80, 240, 80, 30,
    hwnd, (HMENU)1, NULL, NULL);

  INITCOMMONCONTROLSEX icex;

  icex.dwSize = sizeof(icex);
  icex.dwICC  = ICC_DATE_CLASSES;
  InitCommonControlsEx(&icex);

  hMonthCal = CreateWindowW(L"SysMonthCal32", L"",          
    WS_BORDER | WS_CHILD | WS_VISIBLE | MCS_NOTODAYCIRCLE,  
    20, 20, 200, 200, hwnd, (HMENU)2, NULL, NULL);
}

void GetSelectedDate(HWND hMonthCal, HWND hlbl)
{
  SYSTEMTIME time;
  wchar_t date[DATE_SIZE];

  ZeroMemory(&time, sizeof(SYSTEMTIME));
  SendMessage(hMonthCal, MCM_GETCURSEL, 0, (LPARAM) &time);
  
  swprintf(date, DATE_SIZE, L"%d-%d-%d", time.wYear, time.wMonth, time.wDay);
  SetWindowTextW(hlbl, date);
}

In our example, we have two controls. A month calendar control and a static text. The selected date from the month calendar control is dispalyed in the static text.

hMonthCal = CreateWindowW(L"SysMonthCal32", L"",          
  WS_BORDER | WS_CHILD | WS_VISIBLE | MCS_NOTODAYCIRCLE,  
  20, 20, 200, 200, hwnd, (HMENU)2, NULL, NULL);

Here we create a month calendar control. The class name to create a month calender control is L"SysMonthCal32". If we use the MCS_NOTODAYCIRCLE window style, the today's date is not circled.

INITCOMMONCONTROLSEX icex;

icex.dwSize = sizeof(icex);
icex.dwICC  = ICC_DATE_CLASSES;
InitCommonControlsEx(&icex);

To register a month calendar control, we specify the ICC_DATE_CLASSES flag for the dwICC member of the INITCOMMONCONTROLSEX structure.

case WM_NOTIFY:

  lpNmHdr = (LPNMHDR) lParam;

  if (lpNmHdr->code==MCN_SELECT) {
    GetSelectedDate(hMonthCal, hlbl);
  }
  break;

If an event occurs in the month calendar control, the WM_NOTIFY message is sent. The lParam contains a pointer to an NMHDR structure that contains the notification code and additional information.

SendMessage(hMonthCal, MCM_GETCURSEL, 0, (LPARAM) &time);

To fill the structure with the selected date, we send a MCM_GETCURSEL message to the calendar control.

swprintf(date, DATE_SIZE, L"%d-%d-%d", time.wYear,
  time.wMonth, time.wDay);
SetWindowTextW(hlbl, date);

We retrieve the data and set the date to the static text control.

Month Calendar
Figure: Month Calendar

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