Home  Contents

Advanced Windows Controls

In this section of the Winapi C tutorial, we will talk about more advanced windows controls.

Tab Control

A tab control joins multiple windows with corresponding tabs.

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

#define ID_TABCTRL 1
#define EDIT 2
#define BTN_ADD 3
#define BTN_DEL 4
#define BTN_DELALL 5

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HWND hTab, hEdit;
HINSTANCE g_hinst;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "Application" );
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);

  g_hinst = hInstance;
  
  RegisterClass(&wc);
  CreateWindow( wc.lpszClassName, TEXT("Tab Control"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 380, 230, 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)

{

   TCITEM tie;
   TCHAR text[250];
   LRESULT count, id;
   INITCOMMONCONTROLSEX icex;

   switch(msg) {

       case WM_CREATE:
       
           icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
           icex.dwICC = ICC_TAB_CLASSES;
           InitCommonControlsEx(&icex);

          hTab = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD | WS_VISIBLE,
              0, 0, 200, 150, hwnd,(HMENU) ID_TABCTRL, g_hinst, NULL);

          hEdit = CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER,
              250, 20, 100, 25, hwnd, (HMENU) EDIT, g_hinst, NULL);
  
          CreateWindow("button","add", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
              250, 50, 100, 25, hwnd, (HMENU) BTN_ADD, g_hinst, NULL);

          CreateWindow("button", "del", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
              250, 80, 100, 25, hwnd, (HMENU) BTN_DEL, g_hinst, NULL);

          CreateWindow("button","delall", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
              250, 110, 100, 25, hwnd, (HMENU) BTN_DELALL, g_hinst, NULL);
          break;

       case WM_COMMAND:

           switch(LOWORD(wParam)) {

               case BTN_ADD:
                   GetWindowText(hEdit, text, 250);

                   if (lstrlen(text) !=0 ) {
                       tie.mask = TCIF_TEXT;
                       tie.pszText = text;
                       count = SendMessage(hTab, TCM_GETITEMCOUNT, 0, 0);
                       SendMessage(hTab, TCM_INSERTITEM, count, 
                           (LPARAM) (LPTCITEM) &tie);
                   }
                   break;

               case BTN_DEL:
                   id = SendMessage(hTab, TCM_GETCURSEL, 0, 0);
                   if (id != -1) {
                       SendMessage(hTab, TCM_DELETEITEM, 0, id);
                   }
                   break;

               case BTN_DELALL:
                   SendMessage(hTab, TCM_DELETEALLITEMS, 0, 0);
                   break;
           } 
           break;

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

In our example, we use one tab control, one edit control and three buttons. We will dynamically create and delete new tabs on the tab control.

 hTab = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD | WS_VISIBLE,
     0, 0, 200, 150, hwnd,(HMENU) ID_TABCTRL, g_hinst, NULL);

We use the WC_TABCONTROL window class to create a tab control.

if (lstrlen(text) !=0 ) {
    tie.mask = TCIF_TEXT;
    tie.pszText = text;
    count = SendMessage(hTab, TCM_GETITEMCOUNT, 0, 0);
    SendMessage(hTab, TCM_INSERTITEM, count, 
        (LPARAM) (LPTCITEM) &tie);
}

To add a new tab, we fill the TCITEM structure. We provide the type of data to be set (in our case TCIF_TEXT) and the actual text. Then we send two messages. The first message will get the number of tabs on the tab control. It will be used in the second message. The second one will insert a new tab to the control, using the count variable and the TCIF_TEXT structure.

 id = SendMessage(hTab, TCM_GETCURSEL, 0, 0);
 if (id != -1) {
     SendMessage(hTab, TCM_DELETEITEM, 0, id);
 }

To delete an specific tab, we need the current selected tab. We figure it out by sending the TCM_GETCURSEL message to the tab control. To actually delete the tab, we send the TCM_DELETEITEM, specifying the item to be deleted in the wParam parameter.

 SendMessage(hTab, TCM_DELETEALLITEMS, 0, 0);

To delete all tabs from the tab control, we send the TCM_DELETEALLITEMS message.


tab control
Figure: Tab Control

ListBox

A list box is a simple list control, from which user can select one or more items. Selected items are marked.

#include <windows.h>
#include <strsafe.h>

#define IDC_LIST 1
#define IDC_STATIC 2

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

HINSTANCE g_hinst;

typedef struct 
{ 
    TCHAR name[30]; 
    TCHAR job[20]; 
    int age; 

} Friends; 

Friends friends[] = 
{ 
    {TEXT("Erika"), TEXT("waitress"), 18}, 
    {TEXT("Thomas"), TEXT("programmer"), 25}, 
    {TEXT("George"), TEXT("police officer"), 26}, 
    {TEXT("Michael"), TEXT("producer"), 38}, 
    {TEXT("Jane"), TEXT("steward"), 28}, 
}; 

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "Application" );
  wc.hInstance     = hInstance;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc;
  wc.hCursor       = LoadCursor(0, IDC_ARROW);

  g_hinst = hInstance;
  
  RegisterClass(&wc);
  CreateWindow( wc.lpszClassName, TEXT("List Box"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 340, 200, 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)
{

    static HWND hwndList, hwndStatic;
    int i, sel;
    TCHAR buff[100];

    switch(msg) {

        case WM_CREATE:
       
            hwndList = CreateWindow(TEXT("listbox") , NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY,
               10, 10, 150, 120, hwnd,(HMENU) IDC_LIST, g_hinst, NULL);

            hwndStatic = CreateWindow(TEXT("static") , NULL, WS_CHILD | WS_VISIBLE,
               200, 10, 120, 45, hwnd,(HMENU) IDC_STATIC, g_hinst, NULL);

            for (i = 0; i < ARRAYSIZE(friends); i++)  { 
                 SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) friends[i].name);                                 
            } 

            break;
 
        case WM_COMMAND:
            if (LOWORD(wParam) == IDC_LIST) {        
                if (HIWORD(wParam) == LBN_SELCHANGE) {                   
                    sel = (int) SendMessage(hwndList, LB_GETCURSEL, 0, 0);
                    StringCbPrintf(buff, ARRAYSIZE(buff), TEXT("Job: %s\nAge: %d"), 
                        friends[sel].job, friends[sel].age);
                    SetWindowText(hwndStatic, buff);
               }
            }            
            break;

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

In this example, we display a list box control and a static text control. By selecting a person from a list box, we display his job and age in the static control.

 hwndList = CreateWindow(TEXT("listbox") , NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY,
     10, 10, 150, 120, hwnd,(HMENU) IDC_LIST, g_hinst, NULL);

A basic list box is created with listbox window class and LBS_NOTIFY message.

 for (i = 0; i < ARRAYSIZE(friends); i++)  { 
     SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) friends[i].name);                                 
 } 

We fill the list box. To do this, we send multiple LB_ADDSTRING messages to the list box control.

 if (HIWORD(wParam) == LBN_SELCHANGE) {                   
  sel = (int) SendMessage(hwndList, LB_GETCURSEL, 0, 0);
  StringCbPrintf(buff, ARRAYSIZE(buff), TEXT("Job: %s\nAge: %d"), 
      friends[sel].job, friends[sel].age);
  SetWindowText(hwndStatic, buff);
 }

If we select an item from a list box, the window procedure receives a LBN_SELCHANGE message. First we determine the currently selected item by sending a LB_GETCURSEL message to the list box. Then we copy the job name and age from the friends structure to the buff variable. Finally, we set the static text with SetWindowText() function call.


list box
Figure: List Box

In this part of the Winapi tutorial, we have covered advanced Winapi controls.