Controls II.
We continue with Windows Controls. We will show how to use a Trackbar, a Tooltip and a Month calendar control.
Trackbar
A trackbar is a window that contains a slider and optional tick marks. We move the slider using the mouse or keyboard. A trackbar is used to select discrete values from a range of consecutive values. This control is called a slider on other platforms. Only on Windows it is called a trackbar.
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <commctrl.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
HWND hwnd;
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = "Trackbar";
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);
RegisterClass(&wc);
hwnd = CreateWindowEx(WS_EX_LAYERED, wc.lpszClassName, "Trackbar",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 290, 180, 0, 0, hInstance, 0);
SetLayeredWindowAttributes(hwnd, RGB(255, 0, 0), 255, LWA_COLORKEY | LWA_ALPHA);
while( GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
static HWND hwndTrack;
HWND hwndLeftLabel;
HWND hwndRightLabel;
static LRESULT res = 0;
switch(msg)
{
case WM_CREATE:
hwndLeftLabel = CreateWindow("STATIC", "Transparent",
WS_CHILD | WS_VISIBLE,
0, 0, 80, 30,
hwnd, (HMENU)1, NULL, NULL);
hwndRightLabel = CreateWindow("STATIC", "Opaque",
WS_CHILD | WS_VISIBLE,
0, 0, 50, 30,
hwnd, (HMENU)2, NULL, NULL);
InitCommonControls();
hwndTrack = CreateWindowEx(
0, TRACKBAR_CLASS, "Trackbar Control",
WS_CHILD | WS_VISIBLE |
TBS_AUTOTICKS | TBS_ENABLESELRANGE,
100, 25, 120, 30,
hwnd, (HMENU) 3, NULL, NULL);
SendMessage(hwndTrack, TBM_SETRANGE, TRUE, MAKELONG(0, 255));
SendMessage(hwndTrack, TBM_SETPAGESIZE, 0, 20);
SendMessage(hwndTrack, TBM_SETTICFREQ, 20, 0);
SendMessage(hwndTrack, TBM_SETPOS, FALSE, 255);
SendMessage(hwndTrack, TBM_SETBUDDY, TRUE, (LPARAM) hwndLeftLabel);
SendMessage(hwndTrack, TBM_SETBUDDY, FALSE, (LPARAM) hwndRightLabel);
break;
case WM_HSCROLL:
res = SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
SetLayeredWindowAttributes(hwnd, RGB(255, 0, 0), (BYTE) res,
LWA_COLORKEY | LWA_ALPHA);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
In our examle we dislay a trackbar with two static text controls. These controls are attached to the left and to the right of the trackbar. They are called buddies. By dragging the slider, we change the transparency of the window.
When working with transparent windows, we might run into troubles. Our PlatformSDK might be too old. In this case, we must upgrade it. My problem was different. My PlatformSDK was too fresh. I guess. I received the following errors.
.\trackbar.c(24) : error C2065: 'WS_EX_LAYERED' : undeclared identifier .\trackbar.c(27) : warning C4013: 'SetLayeredWindowAttributes' undefined; assuming extern returning int .\trackbar.c(27) : error C2065: 'LWA_COLORKEY' : undeclared identifier .\trackbar.c(27) : error C2065: 'LWA_ALPHA' : undeclared identifier
#define _WIN32_WINNT 0x0500
My workaround was to define a new macro. It is a good idea to look at the header files for identifiers. The definitions were hidden, because my _WIN32_WINNT was bigger that 0x500. I have not the slightest idea why this mess.
hwnd = CreateWindowEx(WS_EX_LAYERED, wc.lpszClassName, "Trackbar",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 290, 180, 0, 0, hInstance, 0);
We must call the CreateWindowEx() with window style WS_EX_LAYERED in order to create window, that's transparency can be modified.
SetLayeredWindowAttributes(hwnd, RGB(255, 0, 0), 255, LWA_COLORKEY | LWA_ALPHA);
The SetLayeredWindowAttributes() function sets the opacity and transparency color key of a layered window. By default, the window is fully transparent. Here we set full opacity.
hwndTrack = CreateWindowEx(
0, TRACKBAR_CLASS, "Trackbar Control",
WS_CHILD | WS_VISIBLE |
TBS_AUTOTICKS | TBS_ENABLESELRANGE,
100, 25, 120, 30,
hwnd, (HMENU) 3, NULL, NULL);
Here we create the trackbar control. We display the ticks when we use the TBS_AUTOTICKS window style.
We are not finished yet. We must call a handful of messages to be able to work with a trackbar. We send a TBM_SETRANGE to set the trackbar range. To set the page size, we send the TBM_SETPAGESIZE message. To set the tick frequency, we send the TBM_SETTICFREQ message. To set the current slider position we send the TBM_SETPOS. Finally we set the trackbar buddies by sending the TBM_SETBUDDY message.
case WM_HSCROLL:
res = SendMessage(hwndTrack, TBM_GETPOS, 0, 0);
SetLayeredWindowAttributes(hwnd, RGB(255, 0, 0), (BYTE) res,
LWA_COLORKEY | LWA_ALPHA);
break;
When we move the trackbar slider, the window procedure receives the WM_HSCROLL message. (In case of a horizontal trackbar, of course.) Here we get the current slider position by sending the TMB_GETPOS message. This value will set the opacity of the window in a SetLayeredWindowAttributes() function call.
A tooltip
A tooltip is a common graphical user element. Tooltip is hidden most of the time. It is a small box that appears near an GUI object when a mouse pointer passes over it. It displays a brief message explaining the object. Tooltips are part of the help system of an application.
#include <windows.h>
#include <commctrl.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateMyTooltip(HWND);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = "Tooltip" ;
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClass(&wc);
CreateWindow( wc.lpszClassName, "Tooltip",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 200, 150, 0, 0, hInstance, 0);
while( GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch(msg)
{
case WM_CREATE:
CreateMyTooltip(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void CreateMyTooltip (HWND hwnd)
{
INITCOMMONCONTROLSEX iccex;
HWND hwndTT;
TOOLINFO ti;
char tooltip[30] = "A main window";
RECT rect;
iccex.dwICC = ICC_WIN95_CLASSES;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
InitCommonControlsEx(&iccex);
hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
0, 0, 0, 0, hwnd, NULL, NULL, NULL );
SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
GetClientRect (hwnd, &rect);
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_SUBCLASS;
ti.hwnd = hwnd;
ti.hinst = NULL;
ti.uId = 0;
ti.lpszText = tooltip;
ti.rect.left = rect.left;
ti.rect.top = rect.top;
ti.rect.right = rect.right;
ti.rect.bottom = rect.bottom;
SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
}
In our example, we set a tooltip for the main window.
INITCOMMONCONTROLSEX iccex; ... iccex.dwICC = ICC_WIN95_CLASSES; iccex.dwSize = sizeof(INITCOMMONCONTROLSEX); InitCommonControlsEx(&iccex);
A tooltip is a part of common controls. We must initialize common controls.
Creation of a tooltip consists of several steps. We must create a tooltip window. Then we make it a topmost window, so that it is not covered by another window. We create a tooltip text and TOOLTIPINFO structure. The structure must be filled with important info. The window handle, tooltip text and the rectangle, which will our tooltip cover. In our example, our tooltip will cover the whole client area of a window.
SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
The tooltip is really added to the window, after we send the TTM_ADDTOOL message.
Month Calendar Control
A Month Calendar is a complex control which is used to select a a date. We can select a date in a simple and intuitive way.
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <tchar.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void GetSelectedDate(HWND, HWND);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG msg ;
WNDCLASS wc = {0};
wc.lpszClassName = TEXT("Month Calendar");
wc.hInstance = hInstance ;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc ;
wc.hCursor = LoadCursor(0,IDC_ARROW);
RegisterClass(&wc);
hwnd = CreateWindow(wc.lpszClassName, TEXT("Month Calendar"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 300, 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 hwndMonthCal;
static HWND hwndLabel;
INITCOMMONCONTROLSEX icex;
LPNMHDR lpNmHdr;
switch(msg)
{
case WM_CREATE:
hwndLabel = CreateWindow(TEXT("STATIC"), TEXT(""),
WS_CHILD | WS_VISIBLE,
80, 240, 80, 30,
hwnd, (HMENU)1, NULL, NULL);
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_DATE_CLASSES;
InitCommonControlsEx(&icex);
hwndMonthCal = CreateWindowEx(0, MONTHCAL_CLASS, TEXT(""),
WS_BORDER | WS_CHILD | WS_VISIBLE | MCS_DAYSTATE,
20, 20, 200, 200, hwnd, (HMENU)2, NULL, NULL);
GetSelectedDate(hwndMonthCal, hwndLabel);
break;
case WM_NOTIFY:
lpNmHdr = (LPNMHDR) lParam;
if (lpNmHdr->code==MCN_SELECT) {
GetSelectedDate(hwndMonthCal, hwndLabel);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void GetSelectedDate(HWND hwndMonthCal, HWND hwndLabel)
{
SYSTEMTIME time;
TCHAR date[50];
ZeroMemory(&time, sizeof(SYSTEMTIME));
SendMessage(hwndMonthCal, MCM_GETCURSEL, 0, (LPARAM) &time);
_stprintf(date, _T("%d/%d/%d"), time.wMonth, time.wDay, time.wYear);
SetWindowText(hwndLabel, date);
}
In our example, we have two controls. A month calendar control and a static text. The selected date from the month calendar control is dispalyed in the static text.
hwndMonthCal = CreateWindowEx(0, MONTHCAL_CLASS, TEXT(""),
WS_BORDER | WS_CHILD | WS_VISIBLE | MCS_DAYSTATE,
20, 20, 200, 200, hwnd, (HMENU)2, NULL, NULL);
Here we create a month calendar control. The important values are MONTHCAL_CLASS and MCS_DAYSTATE.
If we select a date, WM_NOTIFY message is sent.
SYSTEMTIME time; ... SendMessage(hwndMonthCal, MCM_GETCURSEL, 0, (LPARAM) &time);
We define a SYSTEMTIME structure. To fill the structure with selected date, we send a MCM_GETCURSEL message to the calendar control.
stprintf(date, _T("%d/%d/%d"), time.wMonth, time.wDay, time.wYear);
SetWindowText(hwndLabel, date);
We retrieve the data and set the date to the static text control.
In this part of the Winapi tutorial, we have continued covering Winapi controls.