C / C++ Win32 Tab control example [*UPDATED*]

I will be the first to admit that I am NOT even close to having much of a grasp of C/C++ and Win32, but I wanted to share a Tab control example with the world.

When I was trying to understand how tab controls worked in Win32, I scoured the web, and found very little in the way of decent examples. The MSDN examples I found didn’t work, so that made things even harder. So here, for your coding pleasure, is a very simple tab control example.

Forgive me if I have bad coding practices or I’m doing stuff wrong. Just let me know (but try not to toast me to a crisp in doing so).

About the example
I created this example using Code::Blocks, and tried to keep it as simple as possible. I haven’t tried it yet on VC++, so your mileage may vary.

NOTE: In order for this example to work, you will need to tell your linker about the ‘common controls’ library, libcomctl32.a for MinGW, ComCtl32.Lib for VC++, etc. It will fail if you don’t. Also, I only have Windows XP and Vista, and I know it works on those, but it may not work as intended on older versions (let us know, k?).

Here is everything all zipped up: win32-tab-control-example.zip

UPDATE: Here is the Visual C++ 2008 version. Please let me know if you encounter any problems.

Enjoy! :D

Code:


#define _WIN32_IE 0x0500
#include <windows.h>

/*
   *******************************************************
   DON'T FORGET to include the common control library
   in your programming environment, or this example
   will fail.  On MinGW, the library is called:
   libcomctl32.a in the 'lib' folder.  In VC++ 2005 it's:
   ComCtl32.Lib
   *******************************************************
*/

#include <commctrl.h>

/* global variables */
HWND hMainWindow; // main window
HWND hTab;        // our tab control
HWND hTabView1;   // view window for tab1
HWND hTabView2;   // view window for tab2

HINSTANCE hiInst;

/*  declare procedures  */
LRESULT DoTheming ();
LRESULT SetUpControls ();
LRESULT CreateTabControl ();
LRESULT CreateViewWin1 ();
LRESULT CreateViewWin2 ();
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "TabExampleApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    hiInst = hThisInstance;

    /* enable common controls */
    DoTheming();

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hMainWindow = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "Win32 Tab Control Example", /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           800,                 /* The programs width */
           600,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* create all of our example controls */
    SetUpControls();

    /* Make the window visible on the screen */
    ShowWindow (hMainWindow, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}

/* do theming / common controls */
LRESULT DoTheming()
{
    INITCOMMONCONTROLSEX icce;

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

    return 0;
}

/* set up controls */
LRESULT SetUpControls ()
{
    /* once our main window is created,
    create the tab and view windows */
    CreateTabControl();
    CreateViewWin1();
    CreateViewWin2();

    return 0;
}

/* create our tab control */
LRESULT CreateTabControl()
{

    /* create tab control */
    hTab = CreateWindowEx(
        0,                      // extended style
        WC_TABCONTROL,          // tab control constant
        "",                     // text/caption
        WS_CHILD | WS_VISIBLE,  // is a child control, and visible
        5,                      // X position - device units from left
        5,                      // Y position - device units from top
        785,                    // Width - in device units
        555,                    // Height - in device units
        hMainWindow,            // parent window
        NULL,                   // no menu
        hiInst,                 // instance
        NULL                    // no extra junk
        );

    if (hTab == NULL)
    {
        /* tab creation failed -
           Are the correct #defines in your header?
           Have you included the common control library? */
        MessageBox(NULL, "Tab creation failed", "Tab Example", MB_OK | MB_ICONERROR);
        return 0;
    }

    /* start adding items to our tab control */
    TCITEM tie;  // tab item structure

    /* set up our item structure */
    tie.mask = TCIF_TEXT;  // we're only displaying text in the tabs

    /* set up Tab1 item */
    tie.pszText = "Tab1";  // the tab's text/caption
    if (TabCtrl_InsertItem(hTab, 0, &tie) == -1)
    {
        /* couldn't insert tab item */
        DestroyWindow(hTab);
        MessageBox(NULL, "Couldn't add Tab1", "Tab Example", MB_OK | MB_ICONERROR);
        return 0;
    }

    /* set up Tab2 item */
    tie.pszText = "Tab2";  // the tab's text/caption
    if (TabCtrl_InsertItem(hTab, 1, &tie) == -1)
    {
        /* couldn't insert tab item */
        DestroyWindow(hTab);
        MessageBox(NULL, "Couldn't add Tab2", "Tab Example", MB_OK | MB_ICONERROR);
        return 0;
    }

    return 0;
}

/* create a Static control for our View 1 */
LRESULT CreateViewWin1 ()
{
    /* rect structure to hold
       the tab size info */
    RECT tr;

    /* get the tab size info so
       we can place the view window
       in the right place */
    TabCtrl_GetItemRect(hTab, 0, &tr);

    /* create a Static control for our view window */
    hTabView1 = CreateWindowEx(
                    0,
                    "STATIC",
                    "View 1",
                    WS_CHILD | WS_VISIBLE | WS_BORDER | SS_CENTER,
                    325,
                    (tr.bottom - tr.top) + 230,
                    150,
                    20,
                    hTab,
                    NULL,
                    hiInst,
                    NULL
                    );

    return 0;
}

/* create a Static control for our View 2 */

/*
   the WS_VISIBLE constant not used intentionally
   and the control is slightly out of position with
   the first Static control so the tab-switching
   result is clearer
*/
LRESULT CreateViewWin2 ()
{
    /* rect structure to hold
       the tab size info */
    RECT tr;

    /* get the tab size info so
       we can place the view window
       in the right place */
    TabCtrl_GetItemRect(hTab, 0, &tr);

    /* create a Static control for our view window */
    hTabView2 = CreateWindowEx(
                    0,
                    "STATIC",
                    "View 2",
                    WS_CHILD | WS_BORDER | SS_CENTER,
                    300,
                    (tr.bottom - tr.top) + 205,
                    150,
                    20,
                    hTab,
                    NULL,
                    hiInst,
                    NULL
                    );

    return 0;
}

/*  This function is called by the Windows function DispatchMessage()  */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
 /* handle the messages */
    switch (message)
    {

        /* tab message is in WM_NOTIFY message */
        case WM_NOTIFY:

            /* get the tab message from lParam */
            LPNMHDR lpnmhdr = (LPNMHDR)lParam;

            /* if the tab message is TCN_SELCHANGE we'll
               examine it and take the appropriate action */
            if (lpnmhdr->code == TCN_SELCHANGE)
            {
                /* get the currently selected tab item */
                int iPage = TabCtrl_GetCurSel(hTab);

                /* hide/show the appropriate windows based
                   on which tab the user clicks */
                switch (iPage)
                {
                    /* tab 1 (item 0) clicked */
                    case 0:
                        ShowWindow(hTabView2, SW_HIDE);  // first hide View 2
                        ShowWindow(hTabView1, SW_SHOW);  // then show View 1
                        return 0;

                    /* tab 2 (item 1) clicked */
                    case 1:
                        ShowWindow(hTabView1, SW_HIDE);  // first hide View 1
                        ShowWindow(hTabView2, SW_SHOW);  // then show View 2
                        return 0;

                    default:
                        break;
                }
            }
            return 0;

        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

Comments (12)

EvgenyNovember 24th, 2008 at 12:40 am

Hi Will!

Thank you a lot for this tab example! Very straightforward to look and learn!
But there is a little problem imho: with Pelles C it does not want to compile
with error on line 276

LPNMHDR lpnmhdr = (LPNMHDR)lParam;

If you comment this line out
and then replace the line

if (lpnmhdr->code == TCN_SELCHANGE)

like

if (((LPNMHDR)lParam)->code == TCN_SELCHANGE)

then it works. I took it from MSDN.
May it’s very silly, but me is just beginner :)

JamesNovember 24th, 2008 at 2:59 am

Thanks Will,

Appreciate the information, I’m new to Win32 programming and this laid it out nice and clearly.

Tabulicious

Will B.November 24th, 2008 at 12:44 pm

:D Thanks for the comments! If there’s anything I can help with in the future, just ask…I’ll do my best.

asathDecember 17th, 2008 at 3:33 am

Hi,
The example is vey helpful.. I have small doubt.. How to set background color and text color for the tab control?

Thanks

MikeJanuary 9th, 2009 at 3:43 am

Thanks a lot. I’ve been trying to work through the tutorial at this site…

http://www.functionx.com/win32/Lesson03.htm

It is set up for Visual C++.

The first line “#define _WIN32_IE 0×0500″ was very useful here since it must be automatically defined somewhere by the Visual C++ IDE.

Your application looks a little nicer since the control initialisation routines have been separated from the WinMain function.

rogerFebruary 11th, 2009 at 7:37 am

wow a tab example. Why are these so sparse?

SisaMarch 18th, 2009 at 9:27 am

Thanks
I found here exactly I needed.

OlivierMarch 29th, 2009 at 8:20 am

HI Will,
I am Olivier from France, and I thank you for your great job on the tab control programming in Win32,
I have been searching for days , and I found your marvellous article on it, and now I can do it.
You know, I think you are a great programmer and I read about your personal problems in life ,
We could become friend programmer.
I work on C programs in my job, I have already travelled in your country in 1991.
It was a funny and mad trip from New York, LA,SF,Arizona,New Orleans.
God, what a wonderful country !!!.
I hope I will get news from you !!!
God bless you !!!

Will B.March 29th, 2009 at 8:57 am

Olivier, and everyone else who has commented so far, thank you so much! :D

I posted this tab example to help people. I know it was difficult for me to find any good Win32 tab examples on the web, so I didn’t want other people to have the same problem. Hopefully, when time allows, I can put other examples up.

Thank you again, and God bless you all!

OlivierMarch 29th, 2009 at 9:08 am

Hi ,I am happy to get your answer,
Thanks God,since we are friends now, I send you the address of the song of a french singer
who unfortunately died a few days ago, the whole France is crying for this singer
you put this address in http://www.youtube.com
http://www.youtube.com/watch?v=NiOHAlkNZa8
this guy is Alain Bashung
i hope you will enjoy it.

AndreasMay 6th, 2009 at 1:04 am

Here come a huge THANK YOU from me. Ive been searching the net for an example of tabs in c++ the whole day, was about to give up when I hit this page. It is awesome and easy to understand, keep up the good work!

Will B.May 6th, 2009 at 6:47 am

Andreas, you are very welcome. I am not an expert, but I do I enjoy sharing whatever knowledge I do have. :D

Leave a comment

Your comment