Continue with controls
Here we introduce two controls. A group box is a rectangle that surrounds a set of controls. These are often radio buttons. A group box has a label, that describes the control. The purpose of this control is to group controls, that are somehow related. A radio button is a special kind of button, that can be selected by the user, but not cleared. It allows the user to select a single exclusive choice from a group of options.
#include <windows.h>
#define ID_BLUE 1
#define ID_YELLOW 2
#define ID_ORANGE 3
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hinst;
COLORREF g_color;
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT("GroupBox");
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);
g_hinst = hInstance;
RegisterClass(&wc);
hwnd = CreateWindow(wc.lpszClassName, TEXT("GroupBox"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 170, 0, 0, hInstance, 0);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
HDC hdc;
PAINTSTRUCT ps;
HBRUSH hBrush, holdBrush;
HPEN hPen, holdPen;
switch(msg)
{
case WM_CREATE:
CreateWindow(TEXT("button"), TEXT("Choose Color"),
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL);
CreateWindow(TEXT("button"), TEXT("Blue"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 30, 100, 30, hwnd, (HMENU)ID_BLUE , g_hinst, NULL);
CreateWindow(TEXT("button"), TEXT("Yellow"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 55, 100, 30, hwnd, (HMENU)ID_YELLOW , g_hinst, NULL);
CreateWindow(TEXT("button"), TEXT("Orange"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 80, 100, 30, hwnd, (HMENU)ID_ORANGE , g_hinst, NULL);
break;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
switch (LOWORD(wParam)) {
case ID_BLUE:
g_color = RGB(0, 76, 255);
break;
case ID_YELLOW:
g_color = RGB(255, 255, 0);
break;
case ID_ORANGE:
g_color = RGB(255, 123, 0);
break;
}
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hBrush = CreateSolidBrush(g_color);
hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
holdPen = SelectObject(hdc, hPen);
holdBrush = (HBRUSH) SelectObject(hdc, hBrush);
Rectangle(hdc, 160, 20, 260, 120);
SelectObject(hdc, holdBrush);
SelectObject(hdc, holdPen);
DeleteObject(hPen);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example, we have a group box with three radio buttons. By clicking on the radio button, we select a background color for the rectangle on the right.
CreateWindow(TEXT("button"), TEXT("Choose Color"),
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL);
A group box is a special kind of a button with BS_GROUPBOX style.
CreateWindow(TEXT("button"), TEXT("Blue"),
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
20, 30, 100, 30, hwnd, (HMENU)ID_BLUE , g_hinst, NULL);
A radiobutton is also only a special kind of a button with BS_AUTORADIOBUTTON style.
case ID_BLUE:
g_color = RGB(0, 76, 255);
break;
If we click on the radio button, we fill a global variable with a selected color. This variable will be used to create a brush, that will fill the rectangle.
InvalidateRect(hwnd, NULL, TRUE);
We invalidate the rectangle (in this case whole window), which will cause the client area to be redrawn. This will launch a WM_PAINT message. During the WM_PAINT message, we draw the rectangle. Drawing is explained in GDI chapter in more detail.
A combo box is a combination of an edit box or static text and a list. A combo box is used when we need to select an item from a list of available options.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hinst;
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
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);
hwnd = CreateWindow(wc.lpszClassName, TEXT("Combo Box"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 270, 170, 0, 0, hInstance, 0);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
static HWND hwndCombo, hwndStatic;
const TCHAR *items[] = { TEXT("FreeBSD"), TEXT("OpenBSD"), TEXT("Ubuntu"), TEXT("Solaris") };
int i;
LRESULT sel = 0;
switch(msg)
{
case WM_CREATE:
hwndCombo = CreateWindow(TEXT("combobox"), NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
10, 10, 120, 110, hwnd, NULL, g_hinst, NULL);
CreateWindow(TEXT("button"), TEXT("Drop down"),
WS_CHILD | WS_VISIBLE,
150, 10, 90, 25, hwnd, (HMENU)1, g_hinst, NULL);
hwndStatic = CreateWindow(TEXT("static"), TEXT(""),
WS_CHILD | WS_VISIBLE,
150, 80, 90, 25, hwnd, NULL, g_hinst, NULL);
for ( i = 0; i < 4; i++ ) {
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
}
break;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0);
}
if ( HIWORD(wParam) == CBN_SELCHANGE) {
sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
SetWindowText(hwndStatic, items[sel]);
SetFocus(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example, we put three controls on the window. A combo box, a button and a static text. The static text displays the currently selected item from the combo box. It is used to demonstrate the CBN_SELCHANGE combo box message. The button programatically opens the combo box.
hwndCombo = CreateWindow(TEXT("combobox"), NULL,
WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
10, 10, 120, 110, hwnd, NULL, g_hinst, NULL);
To create a combo box, we use the combobox string. We use the CBS_DROPDOWN flag.
for ( i = 0; i < 4; i++ ) {
SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
}
We fill the combo box with items. To add a string to the combo box, we send a CB_ADDSTRING message.
If we select an item from the combo box, the window procedure receives the WM_COMMAND message with the notification message CBN_SELCHANGE in the high-order word of the wParam parameter.
sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0); SetWindowText(hwndStatic, items[sel]); SetFocus(hwnd);
We figure out the currently selected item. We send a CB_GETCURSEL message to the combo box. The function returns the index of the currently selected item. We set the static text to the currently selected string. Finally, we set focus to the main window. By default the combo box has the focus. But it looks ugly, so I changed programatically the focus.
A progress bar is a control that is used, when we process lengthy tasks. It is animated so that the user knows, that our task is progressing.
#include <windows.h>
#include <commctrl.h>
#define ID_BUTTON 1
#define ID_TIMER 2
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hinst;
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
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);
hwnd = CreateWindow(wc.lpszClassName, TEXT("Progress bar"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 260, 170, 0, 0, hInstance, 0);
while( GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
static HWND hwndPrgBar;
static int i = 1;
INITCOMMONCONTROLSEX InitCtrlEx;
InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
InitCtrlEx.dwICC = ICC_PROGRESS_CLASS;
InitCommonControlsEx(&InitCtrlEx);
switch(msg)
{
case WM_CREATE:
hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL,
WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
30, 20, 190, 25, hwnd, NULL, g_hinst, NULL);
CreateWindow(TEXT("button"), TEXT("Start"),
WS_CHILD | WS_VISIBLE,
85, 90, 80, 25, hwnd, (HMENU) 1, g_hinst, NULL);
SendMessage(hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150));
SendMessage(hwndPrgBar, PBM_SETSTEP, 1, 0 );
break;
case WM_TIMER:
SendMessage( hwndPrgBar, PBM_STEPIT, 0, 0 );
i++;
if ( i == 150 )
KillTimer(hwnd, ID_TIMER);
break;
case WM_COMMAND:
i = 1;
SendMessage( hwndPrgBar, PBM_SETPOS, 0, 0 );
SetTimer(hwnd, ID_TIMER, 5, NULL);
break;
case WM_DESTROY:
KillTimer(hwnd, ID_TIMER);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our example, we have a progress bar and a button. The button (re)starts to progress bar control. We use a timer to update the progress bar.
hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL,
WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
30, 20, 190, 25, hwnd, NULL, g_hinst, NULL);
We create a progress bar control with PROGRESS_CLASS class name and PBS_SMOOTH style.
SendMessage( hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150)); SendMessage( hwndPrgBar, PBM_SETSTEP, 1, 0 );
We set the range of the progress bar and it's step.
i = 1; SendMessage( hwndPrgBar, PBM_SETPOS, 0, 0 ); SetTimer(hwnd, ID_TIMER, 5, NULL);
When we press the start button, we set the i value to 1, set the initial position of the progress bar and start the timer. The timer will send periodically a WM_TIMER message to the window procedure. Until it is killed.
case WM_TIMER:
SendMessage( hwndPrgBar, PBM_STEPIT, 0, 0 );
i++;
if ( i == 150 )
KillTimer(hwnd, ID_TIMER);
break;
During the WM_TIMER message, we update the progress bar by one step sending the PBM_STEPIT message. We kill the timer, when the progress bar stops processing.