Hello, I am an Engineering Manager at Facebook with 13+ years in Ad Technology, Natural Language Processing and Data mining. (Learn More)
by Pravin Paratey

Lesson 5 - Adding standard GUI elements

In this lesson, we’ll learn how to add standard GUI elements like the statusbar and the toolbar. We’ll learn about the Common Controls library - which let us add GUI elements like the RichEdit box, the Treeview control, the Listbox/Listview controls and more! Click here to view the full list.

Introducing Common Controls

Windows includes a library called the Common Controls library which lets you add many Windows GUI elements. These elements aren’t part of the core Windows API but are available through a dll - Comctl32.dll.

Before using any UI element from Comctl32.dll, we must load the dll. This can be conveniently done by calling the InitCommonControlsEx() function in MainWindow.cpp:

#include <windows.h>
#include <windowsx.h>
#define _WIN32_IE 0x0500 // To support INITCOMMONCONTROLSEX
#include <commctrl.h>
#include "MainWindow.h"
#include "AboutDialog.h"

First, we include the Commctrl.h, and then add the following code to MainWindow.cpp

bool MainWindow::Run(int nCmdShow)
{
    if(!RegisterClassEx(&m_wndClass))
        return false;

    // Initialize Common controls
    INITCOMMONCONTROLSEX icx;
    // Ensure common control DLL is loaded
    icx.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icx.dwICC = ICC_BAR_CLASSES; // Specify BAR classes
    InitCommonControlsEx(&icx); // Load the common control DLL

    m_hwnd = CreateWindowEx(

Before you hit build, you must add comctl32.lib to your linker path. This is needed to resolve function references as Comctl32.dll is an external library. To do this, go to Project > Build options, select the Linker settings tab, click the Add button and enter libcomctl32.a as illustrated in the figure below.

Adding comctl32.lib

Adding a statusbar

Adding a statusbar is extremely easy. Commctrl.h contains the function CreateStatusWindow() to create a statusbar. So where do we call this function? In lesson 3, we learnt about the Windows Message Queue and how Windows uses it to notify our application of events.

WM_CREATE

We are going to look at two messages that Windows passes to our application. The first is the WM_CREATE. This message is posted to the Message Queue of a window after it has been successfully created. This is where you would want to add any UI elements that you want to create.

    HWND m_hwnd;
    static HWND m_hStatusbar;
HINSTANCE MainWindow::m_hInstance = NULL;
HWND MainWindow::m_hStatusbar;

Lets create a separate function to draw the statusbar,

// Creates the toolbars and statusbar
// Parameters:
//  cs - Contains initialization parameters
// Returns:
//  void
bool MainWindow::OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
    // Create Statusbar
    MainWindow::m_hStatusbar = CreateStatusWindow(WS_CHILD|WS_VISIBLE, "Ready", hwnd, IDC_STATUSBAR);
    return true;
}

Define IDC_STATUSBAR as 102 in resource.h

WM_SIZE

Try resizing the window now. Did you notice that the statusbar does not resize but retains its original size and position?

Breaks on resize

This can be fixed by handing the WM_SIZE message. WM_SIZE is passed to your application whenever the user resizes a window, minimizes or maximizes it.

    switch (msg)
    {
    case WM_SIZE:
        // Resize the statusbar;
		SendMessage(MainWindow::m_hStatusbar,msg,wParam,lParam);
        break;
    case WM_DESTROY:

The statusbar is also a Window which can send and receive messages. We could’ve overridden its message handling proc had we wanted to add custom functionality. For now, we just pass the WM_SIZE message and its parameters to the Statusbar. The statusbar is smart enough to know how to resize itself within its parent container.

LRESULT CALLBACK MainWindow::MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_SIZE:
        // Resize the statusbar
		SendMessage(MainWindow::m_hStatusbar,msg,wParam,lParam);
        break;

Adding toolbars

We are going to add two toolbars to our application. A standard toolbar containing buttons like [New, Open, Save] and a paint toolbar which will contain buttons like [Draw Line, Draw Square, etc]. The first thing to do is add the HWND variables (remember a toolbar is just a specialised Window!) to MainWindow.cpp and MainWindow.h,

HINSTANCE MainWindow::m_hInstance = NULL;
HWND MainWindow::m_hStatusbar = NULL;
HWND MainWindow::m_hMainToolbar = NULL;
HWND MainWindow::m_hPaintToolbar = NULL;
    static HWND m_hStatusbar;
    static HWND m_hMainToolbar;
    static HWND m_hPaintToolbar;
    static char m_szClassName[];

Lets head to our OnCreate method and add code to create a toolbar. This involves,

  1. Creating a toolbar window and specifying it’s style.
  2. Creating an Imagelist to hold the images visible on the toolbar buttons.
  3. Creating a TBBUTTON structure which specify the buttons and their properties.
bool MainWindow::OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
    const int numbuttons1 = 4;
    // Create Main Toolbar
    MainWindow::m_hMainToolbar = CreateWindowEx(
                                    0, TOOLBARCLASSNAME, NULL,
                                    WS_CHILD | TBSTYLE_FLAT,
                                    0, 0, 0, 0,
                                    hwnd, NULL, MainWindow::m_hInstance, NULL);

    HIMAGELIST hImageList1 = ImageList_Create(
            16, 16,                 // 16x16 button size
            ILC_COLOR16 | ILC_MASK, // ILC_MASK ensures transparent background
            numbuttons1, 0); 
    // Set the image list.
    SendMessage(MainWindow::m_hMainToolbar, TB_SETIMAGELIST, (WPARAM)0,
        (LPARAM)hImageList1);

    // Load the button images.
    SendMessage(MainWindow::m_hMainToolbar, TB_LOADIMAGES, 
        (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);

    TBBUTTON tbButtons1[numbuttons1] = {
        {MAKELONG(STD_FILENEW, 0), IDM_FILE_NEW, TBSTATE_ENABLED,
            BTNS_AUTOSIZE, {0}, 0, 0},
        {MAKELONG(STD_FILEOPEN, 0), IDM_FILE_OPEN, TBSTATE_ENABLED,
            BTNS_AUTOSIZE, {0}, 0, 0},
        {MAKELONG(STD_FILESAVE, 0), IDM_FILE_SAVE, TBSTATE_ENABLED,
            BTNS_AUTOSIZE, {0}, 0, 0},
        {MAKELONG(STD_FILESAVE, 0), 0, TBSTATE_ENABLED,
            TBSTYLE_SEP, {0}, 0, 0}
    };

    // Add buttons
    SendMessage(MainWindow::m_hMainToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
    SendMessage(MainWindow::m_hMainToolbar, TB_ADDBUTTONS, (WPARAM)numbuttons1, (LPARAM)&tbButtons1);

    // Show toolbar
    SendMessage(MainWindow::m_hMainToolbar, TB_AUTOSIZE, 0, 0);
    ShowWindow(MainWindow::m_hMainToolbar, TRUE);

Toolbars

Next, we’ll take a look at internationalization and how you can write applications that can be easily ported to other languages.