Windows Programming/User Interface Controls

Some predefined window classes are intended for use as user interface controls. They're commonly known as "standard Windows controls" and "common controls".

Usages of these UI controls should be documented in task-oriented categories, not on an API-oriented basis.

Standard Windows ControlsEdit

Standard Windows controls are implemented in USER32.DLL (former USER.EXE), and later (Windows XP+) subclassified by COMCTL32.DLL for Luna look&feel.

These are always present. No initialization is necessary. Available with Windows 2, ListBox and ComboBox with Windows 3.

Static ControlEdit

Static controls are “controls” with no user interface.

A static control is a child window that displays either text, bitmap image, or icon. Static controls cannot be selected and do not receive focus from the keyboard. They can; however, receive mouse input with use of SS_NOTIFY. This will allow the control to detect click and double-click with the WM_NOTIFY event of its parent.

Static Text (Label)Edit

A label should be placed before the control it refers. When done so, pressing Alt+Hotkey will move the focus to the next possible control automatically with no line of code.

Labels can also be used for showing the appropriate physical unit right to a Single-Line Edit Control. Because such text never acts as a label, the logical order (Z order) is not important. The style bit SS_NOPREFIX can be used to avoid problems with Ampersand (&) character.

Example CodeEdit
Create Label

The label Static Control should be a child of another window, either a main window or a child window. To create it, all you need to do is call CreateWindow() with the STATIC class and the parameters of your choice. Below is the basic code to create it.

/*Create a Static Label control*/

hwndLabel = CreateWindow(
                        TEXT("STATIC"),                   /*The name of the static control's class*/
                        TEXT("Label 1"),                  /*Label's Text*/
                        WS_CHILD | WS_VISIBLE | SS_LEFT,  /*Styles (continued)*/
                        0,                                /*X co-ordinates*/
                        0,                                /*Y co-ordinates*/
                        50,                               /*Width*/
                        25,                               /*Height*/
                        hwnd,                             /*Parent HWND*/
                        (HMENU) ID_MYSTATIC,              /*The Label's ID*/
                        hInstance,                        /*The HINSTANCE of your program*/ 
                        NULL);                            /*Parameters for main window*/

Using the Label

Setting Label's Text

To set the text of your Label (static control), use the SendMessage() function with the WM_SETTEXT message.

/*Setting the Label's text
/*You may need to cast the text as (LPARAM)*/

SendMessage(    hwndLabel ,     /*HWND*/        /*Label*/
                WM_SETTEXT,     /*UINT*/        /*Message*/
                NULL,           /*WPARAM*/      /*Unused*/
       (LPARAM) TEXT("Hello"));  /*LPARAM*/      /*Text*/

Image (Bitmap, Icon, Metafile)Edit

With other style bits, bitmaps or icons can be shown – with no line of code when the source image is placed in the same resource.

Metafile support was added with Windows 95.

Rectangles (Divider lines when tall)Edit

Static controls can be used to draw grouping rectangles or divider lines.

ButtonEdit

Buttons are controls with a simple user interface.

Push ButtonEdit

Everyone should be familiar with the windows push button. It is simply a raised square with text inside it, and when you click usually something happens.

Example CodeEdit

As with all windows controls the push button is a child of your window be it the main window or another child. So to implement it into your program you merely need to call the CreateWindow() function. Below is a line of code to create a button.

Create Button
// create button and store the handle                                                       

HWND hwndButton = CreateWindow (TEXT("button"),                      // The class name required is button
                               TEXT("Push Button"),                  // the caption of the button
                               WS_CHILD |WS_VISIBLE | BS_PUSHBUTTON,  // the styles
                               0,0,                                  // the left and top co-ordinates
                               100,300,                              // width and height
                               hwnd,                                 // parent window handle
                               (HMENU)ID_MYBUTTON,                   // the ID of your button
                               hInstance,                            // the instance of your application
                               NULL) ;                               // extra bits you dont really need

this will create the button for you but it will NOT do anything on a click. In order to do that you need to go into your Windows Procedure function and handle the WM_COMMAND event. In the WM_COMMAND event the low word in the wparam is the ID of the child that has caused the event. So to ensure that you have received a message from your button ensure that the ID's are the same.

Test button ID
// compare button ID to message ID
if(ID_MYBUTTON == LOWORD(wparam))
{ 
   /* it's your button so do some work */ 
}

So now you know that your button has been pressed you need to find out what has happened to it so we process the notification code that is stored in the High Word of the wparam. The notification code you need to watch for is BN_CLICKED.

Test notification
// compare Notification to message Notification
if(BN_CLICKED == HIWORD(wparam))
{ 
   /* the button has been clicked do some stuff */ 
}


One last thing you may need to know is that lparam contains the handle to the button pressed.

Ownerdrawn ButtonEdit

This type can implement anything. In many cases, such buttons are disabled (WS_DISABLED, i.e. no user input) and used to draw anything else into a dialog.

Split buttonEdit

Introduced with Windows Vista, this button has an additional combo box drop-down field, typically to change the behaviour of this push button before pressing.

CheckboxEdit

Checkboxes can be solely or collected to a group. In a group, a multiple-choice selection can be done. Furthermore, a Three-State checkbox is possible. This state should be used for “Don't know”, or “Differently checked choices below” as in Windows Setup Control Panel application.

For a good implementation of checkboxes, following rules should be followed at compile time, typically using a Resource Editor:

  • All checkboxes should havethe BS_AUTOCHECKBOX style bit set, otherwise, program logic must handle clicks
  • A solely checkbox or the first checkbox of a group should have WS_GROUP style bit set
  • Other checkboxes should not have the WS_GROUP style bit
  • Checkbox groups must be contiguously ordered
  • Placing WS_TABSTOP is not mantadory. Typically, every checkbox get this style bit set.

If done so, the Arrow keys will move the focus as expected, and the space bar will toggle its state without one line of code.

A check box group should not be used for more than 7 options. If more options are available, or the count is unknown at compile time, a Checked List Box should be used.

Radio ButtonEdit

Radio buttons are always in a group of at least 2, typically 3..7 choices. They release each-other, and only one can be selected (checked) an one time. Although possible, program logic should avoid that no or more than one radio button is checked. Windows (more precisely, the function IsDialogMessage() wich is automatically processed when calling MessageBox()) can ensure this behaviour with no line of code automatically when:

  • All buttons have BS_AUTORADIOBUTTON style bit set
  • The first Radio Button of a group has WS_GROUP style
  • The other Radio Buttons has no WS_GROUP style
  • The Radio Buttons are contiguous ordered in dialog template resp. CreateWindowEx() calls

Radio buttons should not have the WS_TABSTOP style bit set. Windows automatically sets the focus on TAB key to the selected radio button.

Radio buttons should not be used for more than 7 options. (This is out of the "3..7 choices rule" for good GUI design.) If more options are available, or the count is unknown at compile time, a combo box should be used.

Example CodeEdit

Thats all you need to implement a button in a Win32 Application using the API

Radio Button
HWND hRadio =CreateWindow(TEXT("button"), TEXT("Red"),
                WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
                20, 155, 100, 30, hDlg, (HMENU)ID_RED, GetModuleHandle(NULL), NULL);

Group BoxEdit

This is not a real button and has no interaction at all. It's class name is "BUTTON" too. This element is typically used to visually group radio buttons. It does not create a child element as its .NET counterpart.

Group boxes should be placed before its visual content. When done so, pressing Alt+Hotkey will move the focus to the next possible control automatically, with no line of code.

Scroll BarEdit

Whereas Scroll Bars can be attached to any window or control (and is not a window with a handle itself), a Scroll Bar Control is a true window with a handle. These can be created horizontally (SB_HORZ) or vertically (SB_VERT).

In the days before the Common Controls Progress Bar and Track Bar, someone had “misused” this control to set the volume for a speaker, or to show the progress of a lengthy process. Nowadays, single scroll bars are never used for such purposes.

However, scroll bars are necessary for sharing a Status Bar with a Horizontal Scroll bar, as known from Acrobat Reader or some office software. Because standard scroll bars are always as long as its window, not shorter.

Edit ControlEdit

The Edit control is the standard base object for text editing and display (it is commonly called a TextBox).

The Edit control has a quite large number of styles.

Single-Line EditEdit

Single-line edit controls are typically used for entering short descriptions, file names, parameters, and numbers. Although the style WS_VSCROLL is supported and shows a small vertical scroll bar like an up-down control, nothing else happens. Typically, ES_AUTOHSCROLL is used to move the content horizontally when text does not fit into the given space. The font can be changed programatically, but intermixing fonts and/or colors is not possible. For such purposes, the RichEdit control exists.

Multi-Line EditEdit

Multi-line edits look like Notepad.exe. Indeed, Notepad is just an Edit control with a frame window that supports the menu, load/save, resizing etc. All other functionality, even the context menu, the Unicode and Right-To-Left support, is already built into this control.

// create a text box and store the handle                                                       

HWND hwndText = CreateWindow(
                               TEXT("edit"),                              // The class name required is edit
                               TEXT(""),                                 // Default text.
                               WS_VISIBLE | WS_CHILD | WS_BORDER | WS_HSCROLL | WS_VSCROLL| ES_MULTILINE | ES_AUTOHSCROLL, // the styles
                               0,0,                                      // the left and top co-ordinates
                               100,300,                                  // width and height
                               hwnd,                                     // parent window handle
                               (HMENU)ID_MYTEXT,                         // the ID of your editbox
                               hInstance,                                // the instance of your application
                               NULL
                            );                                   // extra bits you dont really need

Here are the styles used in the example:

WS_BORDER A thin border around the text area.
WS_HSCROLL Display the horizontal scroll bar.
WS_VSCROLL Display the vertical scroll bar.
ES_MULTILINE This is a multiline text box.
ES_AUTOHSCROLL No word wrapping.


        // Set the text.
        SendMessage(hwndText, WM_SETTEXT, 0, (LPARAM)"Hello");

        // Get the text.
        LRESULT iTextSize = SendMessage(hwndText, EM_GETLIMITTEXT, 0, 0);
        char *szText = new char[iTextSize];
        SendMessage(hwndText, WM_GETTEXT, iTextSize, (LPARAM)szText);

List BoxEdit

A list box can have tabulators so it can show somehow tabulated entries. This control is not so commonly used, except in spreadsheet-alike applications. It's main purpose is to deliver the Drop-Down Combo Boxes the pop-up List Box.

Combo BoxEdit

This control has three appearances.

Simple Combo Box (non-editable)Edit

This type is very rarely used and not worth an explanation.

Non-editable Drop-Down Combo BoxEdit

Use this when the user has to choose out of some options.

On creation, use a large height. It sets the maximum height of the dropped-down list box. If less options are available, Windows will shrink its length automatically, but never extends. And scrolling here is annoying. (Note that Windows 3.x doesn't shink.) The height of the remaining control is 13 dialog units. (You need the number 13 when you combine it with similar Edit controls.)

// create a combo box and store the handle                                                       

HWND hwndCombo = CreateWindow (TEXT("combobox"),                         // The class name required is combobox
                               TEXT(""),                                 // not used, ignored.
                               WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, // the styles
                               0,0,                                      // the left and top co-ordinates
                               100,300,                                  // width and height
                               hwnd,                                     // parent window handle
                               (HMENU)ID_MYCOMBO,                        // the ID of your combobox
                               hInstance,                                // the instance of your application
                               NULL) ;                                   // extra bits you dont really need

The displayed height of the actual widget will be automatically changed depending on the font. The "unused" part of the height will be devoted to the size of the drop down menu.

For example, only 33 pixels of the 300 pixel height will be to the selected item. When the down button is clicked, the menu will be 267 pixels in height.

There are other combo box styles: CBS_SIMPLE (similiar to a list box) and CBS_DROPDOWN (similiar to CBS_DROPDOWNLIST but the selected field is editable).

// Add a list of strings to the combo box.

SendMessage(
              hwndCombo,                    // The handle of the combo box
              CB_ADDSTRING,                 // Tells the combo box to append this string to its list
              0,                            // Not used, ignored.
              (LPARAM) "Item A"             // The string to add.
           );

SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) "Item B");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) "Item C");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) "Item D");
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) "Item E");


// Select the default item to be "Item C".
SendMessage(
              hwndCombo,                    // The handle of the combo b,
              CB_SETCURSEL,                 // Tells the combo box to select the specified index
              2,                            // The index of the item to select (starting at zero)
              0                             // Not used, ignored.
           );

Editable Drop-Down ComboboxEdit

Use it when the user can enter single-line text or can take some predefined strings. A typical example is choosing an URL or number with history function. Or selecting a port address, allowing to enter a yet-not-known port address.

Common ControlsEdit

Common controls were introduced roughly with Windows 3.11, and somehow constantly expanded both in functionality and in appearance. Note that, when using Windows XP Luna style or newer, the standard controls above gets automatically subclassified by comctl32.dll's code. A manifest resource controls this behaviour.

The version and functionality of Common Controls DLL heavily depends on version of Internet Explorer installed.

Header and Linking RequirementsEdit

Using Common Controls requires linking to comctl32.lib and including commctrl.h. Whereas some controls are there by default, some other need initialization with InitCommonControlsEx(). It depends on operating system, so it's wise to always call InitCommonControlsEx() with the right bits for the controls your application needs.

The function InitCommonControls() does nothing but ensures that the library is loaded when you don't link to any other Common Controls library function (like CreateToolBar()). Surely, the Windows 3.11 classes were registered at load time of the DLL.

Common controls are typically used like regular dialog elements. The dialog resource template, introduced roughly with Windows 2.0, reserves space for user class names if these are not STATIC, BUTTON, EDIT, LISTBOX, COMBOBOX, or SCROLLBAR, the standard controls above. Therefore, examples may show how to create a child window using CreateWindowEx(), but that's quite uncommon because including them into a dialog template is much easier.

Tool BarEdit

Introduced: Windows 3.11

Status Bar (Status Strip)Edit

Note that the documentation of CalcEffectiveClientRect() is mostly erraneous: The first UINT/BOOL pair of the array is not used at all.

Date/Time PickerEdit

Introduced: Windows 95

IP Address Input FieldEdit

Up/Down ControlEdit

Mostly it's attached to an edit window so it looks like one with a tiny vertical scroll bar. Indeed, the trick with the scroll bar was widely used in the days of Windows 3.x. But now it looks worse than this control.

Up/Down controls automate integer counting and limit watching. They can automatically fit to their “buddy” window, so no code for placement is necessary. Edit controls are automatically shrunken horizontally, as if a vertical scroll bar were added.

Possible buddy windows are single-line edits, progress bars, and trackbars.

Unluckily, Win32 Up/Down Controls won't work with decimals, in opposite to the .NET counterpart. So you have to code a bunch of boring lines to implement .NET behaviour.

Tab ControlEdit

This control is rarely used directly. The Property Sheet uses it heavily.

TooltipEdit

Balloon-style tooltips were introduced quite late, with Windows XP. Therefore, most programs still use standard black-on-yellow rectangular tooltips.

List View, Tree ViewEdit

The Windows Explorer is a perfect example for both controls. The left pane usually shows a directory Tree, and the right pane a file List. Also, the desktop itself is (very similar to a) List View.

static const INITCOMMONCONTROLSEX icc={sizeof(icc),ICC_TREEVIEW_CLASSES};
InitCommonControlsEx(&icc);

Combo Box ExEdit

Checked List BoxEdit

Progress BarEdit

What it isEdit

A standard bar graph that displays progress of an item. Shows a graphical representation of amount completed over amount total.

Example codeEdit

Create Progress Bar

The Progress Bar control should be a child of another window, either a main window or a child window. To create it, all you need to do is call CreateWindow() with the PROGRESS_CLASS class and the parameters of your choice. Below is the basic code to create it.

/*Create a Progress Bar*/

HWND hwndProgress = CreateWindow(
                        PROGRESS_CLASS,         /*The name of the progress class*/
                        NULL,                   /*Caption Text*/
                        WS_CHILD | WS_VISIBLE,  /*Styles*/
                        0,                      /*X co-ordinates*/
                        0,                      /*Y co-ordinates*/
                        200,                    /*Width*/
                        30,                     /*Height*/
                        hwnd,                   /*Parent HWND*/
                        (HMENU) ID_MYPROGRESS,  /*The Progress Bar's ID*/
                        hInstance,              /*The HINSTANCE of your program*/ 
                        NULL);                  /*Parameters for main window*/
Using the Progress Bar
Changing position

There are three ways to increment/decrement the progress bar. PBM_DELTAPOS steps by a given number. PBM_SETPOS sets a specific position. PBM_SETSTEP sets an increment/decrement number, and PBM_STEPIT steps with that number.

Delta Position
PBM_DELTAPOS advances the progress bar with a number given as the wparam.
/*Advance progress bar by 25 units*/

SendMessage(    hwndProgress ,  /*HWND*/        /*Progress Bar*/
                PBM_DELTAPOS,   /*UINT*/        /*Message*/
                25,             /*WPARAM*/      /*Units*/
                NULL)           /*LPARAM*/      /*Unused*/
Set Position
PBM_SETPOS advances the progress bar to the specified position in the WPARAM
/*Advances progress bar to specified position (50)*/

SendMessage(    hwndProgress ,  /*HWND*/        /*Progress Bar*/
                PBM_SETPOS,     /*UINT*/        /*Message*/
                50,             /*WPARAM*/      /*Units*/
                NULL)           /*LPARAM*/      /*Unused*/
Stepping Position
PBM_SETSTEP specifies the amount of units to step. PBM_STEPIT advances by the amount of units given with PBM_SETSTEP (default 10 units).
/*Advances progress bar by specified units*/

/*If progress bar is stepped when at the progress bar's maximum,
/* the progress bar will go back to its starting position*/

                                /*Set the step*/
SendMessage(    hwndProgress ,  /*HWND*/        /*Progress Bar*/
                PBM_SETSTEP,    /*UINT*/        /*Message*/
                1,              /*WPARAM*/      /*Amount to step by*/
                NULL)           /*LPARAM*/      /*Unused*/

                                /*Step*/
SendMessage(    hwndProgress ,  /*HWND*/        /*Progress Bar*/
                PBM_STEPIT,     /*UINT*/        /*Message*/
                NULL,           /*WPARAM*/      /*Unused*/
                NULL)           /*LPARAM*/      /*Unused*/

Next ChapterEdit

ReferencesEdit

MSDN - Windows Controls

Last modified on 7 October 2013, at 07:33