Lesson 3 - Introducing Windows UI Elements
Click here to download the source files to
this tutorial.
This tutorial will teach you to create standard UI elements like a menu,
toolbar and a status bar. You will also learn to separate your code into many
files so that you can manage your code better.
Setting up your workspace
Until now, you wrote all your code in a single file. This is alright for
smaller projects but as the size and complexity of your projects increase,
dividing your code into smaller files makes it easier to maintain your code.
- Create a new blank Windows project and call it Draw. From now on
we will work on this project, adding stuff as we move along.
- Add the following files to the project (Press Ctrl+N to create a new
file and Ctrl+S to save it).
- main.c - This file will contain the
WinMain
function and will be the entry point to our application.
- interface.c - This file will contain interface related stuff
like creating windows and other elements.
- resource.h - This file contains the definitions required for
resources.
- resource.rc - This is the resource script file which defines
resources like icons and dialog boxes.
Your screen should now look something like this,

- Now paste the following code in main.c
/* Lesson 3 - Introducing UI Elements
* Pravin Paratey (May 06, 2003)
**/
#include <windows.h>
HWND hwndMain; //Main window handle
// Windows entry point
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, INT nCmdShow)
{
MSG msg; // MSG structure to store messages
// Create the Main Window
hwndMain = (HWND)CreateMainWindow(hInstance);
if (!hwndMain)
return 0;
// Make the window visible
ShowWindow(hwndMain,SW_SHOW);
// Process messages coming to this window
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// return value to the system
return msg.wParam;
}
And paste this in interface.c
/* interface.c
* Lesson 3 - Introducing UI Elements
* Pravin Paratey (May 06, 2003)
**/
#include <windows.h>
// Callback procedure
LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
// User closed the window
PostQuitMessage(0);
break;
default:
// Call the default window handler
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
// Creates main window
HWND CreateMainWindow(HINSTANCE hInstance)
{
HWND hTmp; // Temporary handle to a window
WNDCLASSEX wcx; // WINDOW class information
// Initialize the struct to zero
ZeroMemory(&wcx,sizeof(WNDCLASSEX));
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_HREDRAW|CS_VREDRAW |CS_DBLCLKS ; // Class styles
wcx.lpfnWndProc = (WNDPROC)MainWndProc; // Pointer to the callback proc
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance; // Instance of the application
wcx.hIcon = NULL; // Class Icon
wcx.hCursor = LoadCursor(NULL, IDC_ARROW); // Class Cursor
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW); // Background brush
wcx.lpszMenuName = NULL; // Menu resource
wcx.lpszClassName = "Draw"; // Name of this class
wcx.hIconSm = NULL; // Small icon for this class
// Register this window class with MS-Windows
if (!RegisterClassEx(&wcx))
return 0;
// Create the window
hTmp = CreateWindowEx(0, //Extended window style
"Draw", // Window class name
"Draw v0.1", // Window title
WS_OVERLAPPEDWINDOW, // Window style
CW_USEDEFAULT,CW_USEDEFAULT, // (x,y) pos of window
CW_USEDEFAULT,CW_USEDEFAULT, // Width, height of window
HWND_DESKTOP, // HWND of the parent
NULL, // Handle to menu
hInstance, // Handle to application instance
NULL); // Pointer to window creation data
// TIP : We dont want to make the window visible yet so that all the
// controls can be placed and sized
// Create Toolbar
// Create Statusbar
// return value
return hTmp;
}
Notice that this code is the same as that in Lesson
2, split into two files.
Now that you've set up your workspace, its time to learn some stuff.
Creating Menus
Creating static menus (i.e. menus that do not dynamically change during the
life of the application) is amazingly simple. But before we go into that, I'll
have to tell you a bit about resources. I'll not go in depth here because its
something we cover in a tutorial later.
Resources
Resources are data that you can add to the applications executable file.
Resources can be:
- standard - icon, cursor, menu, dialog box, bitmap, enhanced
metafile, font, accelerator table, message-table entry, string-table entry,
or version
- custom - any kind of data that doesn't fall into the previous
category (for example a mp3 file or a dictionary database).
Creating a menu
Creating a static menu involves creating a menu resource and adding the
menu resource to the window's menu. Add the following code to resource.rc:
/* resource.rc
**/
#include <windows.h>
#include "resource.h"
IDMAINMENU MENU DISCARDABLE LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&New\tCtrl+N", IDM_FILE_NEW
MENUITEM "&Open\tCtrl+O", IDM_FILE_OPEN
MENUITEM "&Save\tCtrl+S", IDM_FILE_SAVE
MENUITEM SEPARATOR
MENUITEM "E&xit", IDM_FILE_EXIT
END
POPUP "&Help"
BEGIN
MENUITEM "&About\tF1", IDM_HELP_ABOUT
END
END
Look at the structure of a menu resource. IDMAINMENU,
IDM_FILE_NEW, IDM_FILE_OPEN, etc are numeric identifiers
(or numbers). They will be defined in the file resource.h. MENU,
POPUP and MENUITEM are keywords. To learn more about
them, look them up in your Win32 docs or on MSDN under Resources.
i Numeric Identifiers
Terms like IDMAINMENU, IDM_FILE_NEW, IDM_FILE_OPEN, etc are called
numeric identifiers. You are free to give them any name you please. For
instance, IDMAINMENU could have very well been called RUBBER_DUCKY. Numeric
Identifiers are used to make it easier to code and understand. Besides, it
is much easier to write IDMAINMENU instead of memorizing it as the number
300 (or looking it up every time you need it).
Switch to the file resource.h. This is where we'll give numbers to
the constant identifiers. You are free to give any number you want. I have
numbered them 300 onwards. Add the code that follows:
/* resource.h
**/
#define IDMAINMENU 300
#define IDM_FILE_NEW 301
#define IDM_FILE_OPEN 302
#define IDM_FILE_SAVE 303
#define IDM_FILE_EXIT 304
#define IDM_HELP_ABOUT 305
That done, all we've got to do is tell our main window to use this menu:
/* interface.c
**/
...
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW); // Background brush
wcx.lpszMenuName = MAKEINTRESOURCE(IDMAINMENU); // Menu resource
wcx.lpszClassName = "Draw"; // Name of this class
...
Compile and run your application. You should see your menu :-).

i Info: Compiler *.res Errors
Did you ever get an error like
"[Build Error] [Draw_private.res] Error 1" on the Compiler Tab
followed by
"Line 20 in file resource.rc : parse error" on the Resources
tab?
It happens if you forget to define a constant identifier.

†
Exercise
Now that you've learnt how a menu is added, add the following menu items.
You are free to number them as you wish -
| Edit
|
ID |
Help |
ID |
Undo
Redo
---
Cut
Copy
Paste
---
Select All |
ID_EDIT_UNDO
ID_EDIT_REDO
ID_EDIT_CUT
ID_EDIT_COPY
ID_EDIT_PASTE
ID_EDIT_SELECTALL |
Contents
Index
---
About |
ID_HELP_CONTENTS
ID_HELP_INDEX
ID_HELP_ABOUT |
Common Controls
Common controls are classes provided by Windows to let you create and use
elements like toolbars and status bars easily. You interact with these
controls by sending messages.
The following table lists the Windows controls. You can use these controls
in your applications.
| Control |
Description |
| Animation |
An animation control is a window that displays an
Audio-Video Interleaved (AVI) clip. |
| Button |
Button controls typically notify the parent window when
the user chooses the control. |
| Combo Box |
Combo box controls are a combination of list boxes and
edit controls, letting the user choose and edit items. |
| ComboBoxEx |
ComboBoxEx Controls are an extension of the combo box
control that provides native support for item images. |
| Date and Time Picker |
A date and time picker (DTP) control provides a simple
and intuitive interface through which to exchange date and time
information with a user. |
| Drag List Box |
Drag List Boxes are a special type of list box that
enables the user to drag items from one position to another. |
| Edit |
Edit controls let the user view and edit text. |
| Flat Scroll Bar |
Flat scroll bars behave just like standard scroll bars
except that you can customize their appearance to a greater extent than
standard scroll bars. |
| Header |
A header control is a window that is usually positioned
above columns of text or numbers. It contains a title for each column, and
it can be divided into parts. |
| Hot Key |
A hot key control is a window that enables the user to
enter a combination of keystrokes to be used as a hot key. |
| Image Lists |
An image list is a collection of images of the same
size, each of which can be referred to by its index. |
| IP Address Controls |
An Internet Protocol (IP) address control allows the
user to enter an IP address in an easily understood format. |
| List Box |
List box controls display a list from which the user
can select one or more items. |
| List-View |
A list-view control is a window that displays a
collection of items. The control provides several ways to arrange and
display the items. |
| Month Calendar |
A month calendar control implements a calendar-like
user interface. |
| Pager |
A pager control is a window container that is used with
a window that does not have enough display area to show all of its
content. |
| Progress Bar |
A progress bar is a window that an application can use
to indicate the progress of a lengthy operation. |
| Property Sheets |
A property sheet is a window that allows the user to
view and edit the properties of an item. |
| ReBar |
Rebar controls act as containers for child windows. An
application assigns child windows, which are often other controls, to a
rebar control band. |
| Rich Edit |
Rich Edit controls let the user view and edit text with
character and paragraph formatting, and can include embedded COM objects. |
| Scroll Bars |
Scroll bars let the user choose the direction and
distance to scroll information in a related window. |
| Static |
Static controls often act as labels for other controls. |
| Status Bars |
A status bar is a horizontal window at the bottom of a
parent window in which an application can display various kinds of status
information. |
| SysLink |
A SysLink control provides a convenient way to embed
hypertext links in a window. |
| Tab |
A tab control is analogous to the dividers in a
notebook or the labels in a file cabinet. By using a tab control, an
application can define multiple pages for the same area of a window or
dialog box. |
| Toolbar |
A toolbar is a control window that contains one or more
buttons. Each button, when clicked by a user, sends a command message to
the parent window. |
| ToolTip |
ToolTips are hidden most of the time. They appear
automatically, or pop up, when the user pauses the mouse pointer over a
tool. |
| Trackbar |
A trackbar is a window that contains a slider and
optional tick marks. When the user moves the slider, using either the
mouse or the direction keys, the trackbar sends notification messages to
indicate the change. |
| Tree-View |
A tree-view control is a window that displays a
hierarchical list of items, such as the headings in a document, the
entries in an index, or the files and directories on a disk. |
| Up-Down |
An up-down control is a pair of arrow buttons that the
user can click to increment or decrement a value, such as a scroll
position or a number displayed in a companion control. |
Creating Toolbars
Toolbars are part of Windows common controls. A toolbar is created by
calling the CreateWindow() function and specifying the classname
as TOOLBARCLASSNAME. Before we do this, we have to ensure that the Windows
common controls dll is loaded and that the TOOLBAR class is loaded.
Add an empty function named HWND CreateMainWindowToolbar(HWND hParent)
in interface.c and add the following code.
HWND CreateMainWindowToolbar(HWND hParent)
{
HWND hTmp; // Temporary HWND
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
// Create the toolbar window
hTmp = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL,
WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hParent,
(HMENU) NULL, NULL, NULL);
}
Before you hit compile, there are a few things that must be done,
- Add the following lines at the top of interface.c
1 /* interface.c
2 * Lesson 3 - Introducing UI Elements
3 * Pravin Paratey (May 06, 2003)
4 **/
5 #define _WIN32_IE 0x0500 // To support INITCOMMONCONTROLSEX
7 #include <windows.h>
8 #include <commctrl.h>
Note: If you are running Windows 9x with IE 4, define _WIN32_IE as
0x0400 and for plain old Windows 95 comment it out.
- Add libcomctl32.a to the linker libraries so that the linker knows where
to find them. To do this, select Project->Project Options (or press Alt+P)
and then on the tab marked Parameters, click on the Add Library button.

On the dialog that appears, navigate to your Dev-Cpp/lib folder and
select libcomctl32.a.
- That done, declare
hToolbar as a global variable of type
HWND and call CreateMainWindowToolbar() from
CreateMainWindow()./* interface.c
**/
#include ...
HWND hToolbar;
...
HWND CreateMainWindow(HINSTANCE hInstance)
{ ...
// Create Toolbar
hToolbar = CreateMainWindowToolbar(hTmp);
...
- If you press F9 now, you'll see a toolbar. However, when you resize the
window, the toolbar will not resize with it. Getting the toolbar to resize
itself is a simple matter of handling the
WM_SIZE message and
passing this message onto the toolbar:
// Callback procedure
LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
// User closed the window
PostQuitMessage(0);
break;
case WM_SIZE:
// Resize the toolbar
SendMessage(hToolbar,msg,wParam,lParam);
break;
default:
// Call the default window handler
return DefWindowProc(hwnd,msg,wParam,lParam);
}
Compile and run your application. You should see a band for the toolbar.
The toolbar doesn't have any buttons yet, and that's what we are going to do
next.
Adding buttons to the toolbar
Add the following to interface.c (Yeah, I know it looks scary):
#define NUM_BUTTONS 14
HWND CreateMainWindowToolbar(HWND hParent)
{
HWND hTmp; // Temporary HWND
INITCOMMONCONTROLSEX icx;
TBADDBITMAP tbab;
TBBUTTON tbb[NUM_BUTTONS];
int i;
// 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
// Create the toolbar window
hTmp = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL,
WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | WS_BORDER,
0, 0, 0, 0, hParent, (HMENU) NULL, NULL, NULL);
// Send the TB_BUTTONSTRUCTSIZE message, which is required for
// backward compatibility.
SendMessage(hTmp, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
// Add the bitmap containing button images to the toolbar.
tbab.hInst = HINST_COMMCTRL;
tbab.nID = IDB_STD_SMALL_COLOR;
SendMessage(hTmp, TB_ADDBITMAP, (WPARAM) NUM_BUTTONS,(LPARAM) &tbab);
// Add buttons
ZeroMemory(&tbb, sizeof(TBBUTTON)*NUM_BUTTONS);
for(i=0;i<14;i++)
{
tbb[i].fsState = TBSTATE_ENABLED;
tbb[i].fsStyle = TBSTYLE_BUTTON;
}
tbb[0].iBitmap = STD_FILENEW;
tbb[0].idCommand = IDS_NEW;
tbb[1].iBitmap = STD_FILEOPEN;
tbb[1].idCommand = IDS_OPEN;
tbb[2].iBitmap = STD_FILESAVE;
tbb[2].idCommand = IDS_SAVE;
tbb[3].iBitmap = 0;
tbb[3].idCommand = 0;
tbb[3].fsStyle = TBSTYLE_SEP;
tbb[4].iBitmap = STD_PRINTPRE;
tbb[4].idCommand = IDS_PRINTPRE;
tbb[5].iBitmap = STD_PRINT;
tbb[5].idCommand = IDS_PRINT;
tbb[6].iBitmap = 0;
tbb[6].idCommand = 0;
tbb[6].fsStyle = TBSTYLE_SEP;
tbb[7].iBitmap = STD_CUT;
tbb[7].idCommand = IDS_CUT;
tbb[8].iBitmap = STD_COPY;
tbb[8].idCommand = IDS_COPY;
tbb[9].iBitmap = STD_PASTE;
tbb[9].idCommand = IDS_PASTE;
tbb[10].iBitmap = 0;
tbb[10].idCommand = 0;
tbb[10].fsStyle = TBSTYLE_SEP;
tbb[11].iBitmap = STD_UNDO;
tbb[11].idCommand = IDS_UNDO;
tbb[12].iBitmap = STD_REDOW;
tbb[12].idCommand = IDS_REDO;
tbb[13].iBitmap = 0;
tbb[13].idCommand = 0;
tbb[13].fsStyle = TBSTYLE_SEP;
// Add buttons
SendMessage(hTmp,TB_ADDBUTTONS,NUM_BUTTONS,(LPARAM)&tbb);
return hTmp;
}
And the following to resource.h:
#define IDS_NEW 200
#define IDS_OPEN 201
#define IDS_SAVE 202
#define IDS_PRINTPRE 203
#define IDS_PRINT 204
#define IDS_CUT 205
#define IDS_COPY 206
#define IDS_PASTE 207
#define IDS_UNDO 208
#define IDS_REDO 209
Hit F9. Your app should look something like this:

Creating a status bar
Creating a status bar doesn't involve as much code as a toolbar. We can
make a simple status bar with just one line of code:
HWND CreateStatusWindow(
LONG style,
LPCTSTR lpszText,
HWND hwndParent,
UINT wID
);
Notice the word simple. Ok, I'll not scare you just yet. We're going
to make a simple status bar for now. Enter the following code in
interface.c:
HWND hToolbar;
HWND hStatusbar;
...
// Callback procedure
LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
...
case WM_SIZE:
// Resize the toolbar
SendMessage(hToolbar,msg,wParam,lParam);
// Resize the statusbar;
SendMessage(hStatusbar,msg,wParam,lParam);
...
}
...
// Creates main window
HWND CreateMainWindow(HINSTANCE hInstance)
{
...
// Create Toolbar
hToolbar = CreateMainWindowToolbar(hTmp);
// Create Statusbar
hStatusbar = CreateMainWindowStatusbar(hTmp);
// return value
return hTmp;
}
...
HWND CreateMainWindowStatusbar(HWND hParent)
{
return CreateStatusWindow(
WS_CHILD|WS_VISIBLE,"Ready",hParent,IDM_STATUSBAR);
}
There's one last thing you need to do. Can you guess what it is? I'll give
you a hint. It's marked in RED. Yep! Add a
#define IDM_STATUSBAR to resource.h. Give it any number you
want. I have given it 299.

So you've got a pretty toolbar, a menu and a status bar. All you need to do
now is add code to perform an action when the user interacts with them.
Handling user interactions
Every time a user clicks on a menu item or a button on the toolbar, a
WM_COMMAND message is sent to your application. The WM_COMMAND
message looks like:
WM_COMMAND
wNotifyCode = HIWORD(wParam); // notification code
wID = LOWORD(wParam); // item, control, or accelerator identifier
hwndCtl = (HWND) lParam; // handle of control
Parameters
- wNotifyCode
- Value of the high-order word of wParam. Specifies the notification code
if the message is from a control. If the message is from an accelerator,
this parameter is 1. If the message is from a menu, this parameter is 0.
- wID
- Value of the low-order word of wParam. Specifies the identifier of the
menu item, control, or accelerator.
- hwndCtl
- Value of lParam. Identifies the control sending the message if the
message is from a control. Otherwise, this parameter is NULL.
While we could work with wParam and lParam ourselves, Windows provides a
better mechanism for handling WM_ messages - through the
HANDLE_WM_ macro.
For WM_COMMAND, we'll use the HANDLE_WM_COMMAND
macro defined as
HANDLE_WM_COMMAND(HWND hwnd, WPARAM wParam, LPARAM lParam,
(void *)OnCommand (HWND hwnd, int id, HWND hCtl, UINT codeNotify))
Paste the code that follows in interface.c:
#include <windowsx.h>
...
// Handler for WM_COMMAND
void OnCommand(HWND hwnd, int id, HWND hCtl, UINT codeNotify)
{
switch(id)
{
case IDM_FILE_EXIT:
PostQuitMessage(0);
break;
}
}
// Callback procedure
LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND: // Handles user input
HANDLE_WM_COMMAND(hwnd, wParam, lParam, OnCommand);
break;
case WM_DESTROY:
...
Hit F9 to compile and run. You'll see that clicking on File -> Exit now
quits the application.
windowsx.h
This header files contains many macros designed to make coding easier. Take
a look at it. Some of the stuff in there is really interesting. You'll find it
in the include folder of your development environment.
This concludes this lesson. You've learnt a lot today. You can now create
basic Windows UI elements and can write code to perform an action when the
user interacts with them. In the next lesson, we'll take a look at resources
and how they can make certain aspects of programming easier. We'll also take a
look at internationalization issues and how to write code that's easy to port
to other languages and locales.
Next Lesson4