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. Each page consists of a certain type of information or a group of controls that the application displays when the user selects the corresponding tab.
You can create a tab control by calling the
You send messages to a tab control to add tabs and otherwise affect the control's appearance and behavior. Each message has a corresponding macro that you can use instead of sending the message explicitly. You cannot disable an individual tab in a tab control. However, you can disable a tab control in a property sheet by disabling the corresponding page.
You can apply certain characteristics to tab controls by specifying tab control styles when the control is created. For example, you can specify the alignment and general appearance of the tabs in a tab control.
You can cause the tabs to look like buttons by specifying the
You can cause a tab to receive the input focus when clicked by specifying
the
By default, a tab control displays only one row of tabs. If not all tabs
can be shown at once, the tab control displays an up-down control so that the
user can scroll additional tabs into view. You can cause a tab control to
display multiple rows of tabs, if necessary, by specifying the
A tab control automatically sizes each tab to fit its icon, if any, and its
label. To give all tabs the same width, you can specify the
You can specify that the parent window draws the tabs in the control by
using the
You can specify that a tab control will create a ToolTip control by using
the
Each tab in a tab control consists of an icon, a label, and
application-defined data. This information is specified by a
To add tabs to a tab control, use the
You can retrieve the current number of tabs by using the
You can associate application-defined data with each tab. For example, you
might save information about each page with its corresponding tab. By default,
a tab control allocates four extra bytes per tab for application-defined data.
You can change the number of extra bytes per tab by using the
The application-defined data is specified by the lParam member of the TCITEM structure. If you use more than 4 bytes of application-defined data, you need to define your own structure and use it instead of TCITEM. You can retrieve and set application-defined data the same way you retrieve and set other information about a tab—by using the TCM_GETITEM and TCM_SETITEM messages.
The first member of your structure must be a
The display area of a tab control is the area in which an application
displays the current page. Typically, an application creates a child window or
dialog box, setting the window size and position to fit the display area.
Given the window rectangle for a tab control, you can calculate the bounding
rectangle of the display area by using the
Sometimes the display area must be a particular size—for example, the size of a modeless child dialog box. Given the bounding rectangle for the display area, you can use TCM_ADJUSTRECT to calculate the corresponding window rectangle for the tab control.
When the user selects a tab, a tab control sends its parent window
notification messages in the form of
You can process TCN_SELCHANGING to save the state of the outgoing page. You can return TRUE to prevent the selection from changing. For example, you might not want to switch away from a child dialog box in which a control has an invalid setting.
You must process TCN_SELCHANGE to display the incoming page in the display area. This might simply entail changing the information displayed in a child window. More often, each page consists of a child window or dialog box. In this case, an application might process this notification by destroying or hiding the outgoing child window or dialog box and by creating or showing the incoming child window or dialog box.
You can retrieve and set the current selection by using the
Each tab can have an icon associated with it, which is specified by an
index in the image list for the tab control. When a tab control is created, it
has no image list associated with it. An application can create an image list
by using the
You can add images to a tab control's image list just as you would to any
other image list. However, an application should remove images by using the
Destroying a tab control does not destroy an image list that is associated with it. You must destroy the image list separately. This is useful if you want to assign the same image list to multiple tab controls.
To retrieve the handle to the image list currently associated with a tab
control, you can use the
Each tab in a tab control has a size and position. You can set the size of tabs, retrieve the bounding rectangle of a tab, or determine which tab is at a specified position.
For fixed-width and owner-drawn tab controls, you can set the exact width
and height of tabs by using the TCM_SETITEMSIZE message. In other tab
controls, each tab's size is calculated based on the icon and label for the
tab. The tab control includes space for a border and an additional margin. You
can set the thickness of the margin by using the
You can determine the current bounding rectangle for a tab by using the
In a tab control with the TCS_MULTILINE style, you can determine the
current number of rows of tabs by using the
If a tab control has the TCS_OWNERDRAWFIXED style, the parent window must
paint tabs by processing the
By default, the itemData member of DRAWITEMSTRUCT contains the value of the lParam member of the TCITEM structure. However, if you change the amount of application-defined data per tab, itemData contains the address of the data instead. You can change the amount of application-defined data per tab by using the TCM_SETITEMEXTRA message.
To specify the size of items in a tab control, the parent window must
process the
You can use a ToolTip control to provide a brief description of each tab in a tab control. A tab control that has the TCS_TOOLTIPS style creates a ToolTip control when it is created and destroys the ToolTip control when it is destroyed. You can also create a ToolTip control and assign it to a tab control.
If you use a ToolTip control with a tab control, the parent window must
process the
To use the same ToolTip control with more than one tab control, create the
ToolTip control yourself and assign it to the tab control by using the
This section describes the message processing performed by a tab control. Messages specific to tab controls are discussed in other sections of this documentation.
| Message | Processing performed |
|---|---|
| Does nothing if the tab control released the mouse capture itself. If another window captured the mouse and a button is held down, the command releases the button. | |
| Allocates and initializes an internal data structure. The control creates a ToolTip control if the TCS_TOOLTIPS style is specified. | |
| Frees resources allocated during WM_CREATE processing. | |
| Returns a combination of the DLGC_WANTARROWS and DLGC_WANTCHARS values. | |
| Returns the handle to the font used for labels. | |
| Processes direction keys and changes the selection, if appropriate. | |
| Invalidates the tab that has the focus so it will be repainted to reflect an unfocused state. | |
| Forwards the message to the ToolTip control, if any, and changes the selection if the user is clicking a tab. If the user is clicking a button, the control redraws the button to give a sunken appearance and captures the mouse. If the user is clicking either a tab or button and the TCS_FOCUSONBUTTONDOWN style is specified, the control sets the focus to itself. | |
| Releases the mouse if a button was pressed. If the cursor is over the button and is being held down, the control changes the selection accordingly and redraws the button. | |
| Forwards the message to the ToolTip control, if any. If the TCS_BUTTONS style is specified and the mouse button is being held down after clicking, the control may also redraw the affected button to give it a raised or sunken appearance. | |
| Forwards notification messages sent by the ToolTip control. | |
| Draws a border around the display area (unless the TCS_BUTTONS style is specified) and paints any tabs that intersect the invalid rectangle. For each tab, it draws the body of the tab (or sends a WM_DRAWITEM message to the parent window) and then draws a border around the tab. If the wParam parameter is non-NULL, the control assumes that the value is an HDC and paints using that device context. | |
| Sends an |
|
| Invalidates the tab that has the focus so that it will be repainted to reflect a focused state. | |
| Sets the font used for labels. | |
| Sets the state of an internal flag that determines whether the control is repainted when items are inserted and deleted, when the font is changed, and so on. | |
| Recalculates the positions of tabs and may invalidate part of the tab control to force repainting of some or all tabs. |
This section provides two examples that use tab controls. The first example demonstrates how to use a tab control to switch between multiple pages of text in an application's main window. The second example demonstrates how to use a tab control to switch between multiple pages of controls in a dialog box.
The example in this section demonstrates how to create a tab control and
display it in the client area of the application's main window. The
application displays a third window (a static control) in the display area of
the tab control. The parent window positions and sizes the tab control and
static control when it processes the
There are seven tabs, one for each day of the week. When the user selects a tab, the application displays the name of the corresponding day in the static control. The following global variables are used in this example.
// Global variables
HINSTANCE g_hinst; // handle to application instance
char g_achTemp[256]; // temporary buffer for strings
HWND g_hwndMain; // main application window
HWND g_hwndTab; // tab control
HWND g_hwndDisplay; // handle to static control in
// tab control's display area
The following function creates the tab control and adds a tab for each day
of the week. The names of the days are defined as string resources,
consecutively numbered starting with IDS_FIRSTDAY (defined in the
application's header file). Both the parent window and the tab control must
have the
// DoCreateTabControl - creates a tab control, sized to fit the
// specified parent window's client area, and adds some tabs.
// Returns the handle to the tab control.
// hwndParent - parent window (the application's main window).
HWND WINAPI DoCreateTabControl(HWND hwndParent)
{
RECT rcClient;
HWND hwndTab;
TCITEM tie;
int i;
// Get the dimensions of the parent window's client area, and
// create a tab control child window of that size.
GetClientRect(hwndParent, &rcClient);
InitCommonControls();
hwndTab = CreateWindow(
WC_TABCONTROL, "",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, rcClient.right, rcClient.bottom,
hwndParent, NULL, g_hinst, NULL
);
if (hwndTab == NULL)
return NULL;
// Add tabs for each day of the week.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = g_achTemp;
for (i = 0; i < 7; i++) {
LoadString(g_hinst, IDS_FIRSTDAY + i,
g_achTemp, sizeof(g_achTemp)/sizeof(g_achTemp[0]));
if (TabCtrl_InsertItem(hwndTab, i, &tie) == -1) {
DestroyWindow(hwndTab);
return NULL;
}
}
return hwndTab;
}
The following function creates the static control that occupies the tab control's display area. The application's initialization function calls this function after creating the main window and the tab control.
// DoCreateDisplayWindow - creates a child window (a static
// control) to occupy the tab control's display area.
// Returns the handle to the static control.
// hwndParent - parent window (the application's main window).
HWND WINAPI DoCreateDisplayWindow(HWND hwndParent)
{
HWND hwndStatic = CreateWindow("STATIC", "",
WS_CHILD | WS_VISIBLE | WS_BORDER,
0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
hwndParent, NULL, g_hinst, NULL);
return hwndStatic;
}
Following are the relevant portions of the application's window procedure.
The application processes the WM_SIZE message to position and size the
tab control and the static control. To determine the appropriate position and
size for the static control, this example sends the tab control a TCM_ADJUSTRECT
message (by using the
When a tab is selected, the tab control sends a WM_NOTIFY message, specifying the TCN_SELCHANGE notification message. The application processes this notification message by setting the text of the static control.
// MainWindowProc - processes the message for the main window class.
// The return value depends on the message.
// hwnd - handle to the window.
// uMsg - identifier for the message.
// wParam - message-specific parameter.
// lParam - message-specific parameter.
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg) {
case WM_SIZE: {
HDWP hdwp;
RECT rc;
// Calculate the display rectangle, assuming the
// tab control is the size of the client area.
SetRect(&rc, 0, 0,
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
TabCtrl_AdjustRect(g_hwndTab, FALSE, &rc);
// Size the tab control to fit the client area.
hdwp = BeginDeferWindowPos(2);
DeferWindowPos(hdwp, g_hwndTab, NULL, 0, 0,
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),
SWP_NOMOVE | SWP_NOZORDER
);
// Position and size the static control to fit the
// tab control's display area, and make sure the
// static control is in front of the tab control.
DeferWindowPos(hdwp,
g_hwndDisplay, HWND_TOP, rc.left, rc.top,
rc.right - rc.left, rc.bottom - rc.top, 0
);
EndDeferWindowPos(hdwp);
}
break;
case WM_NOTIFY:
switch (HIWORD(wParam)) {
case 0:
.
. // menu command processing
.
case TCN_SELCHANGE: {
int iPage = TabCtrl_GetCurSel(g_hwndTab);
LoadString(g_hinst, IDS_FIRSTDAY + iPage,
g_achTemp,
sizeof(g_achTemp)/sizeof(g_achTemp[0]));
SendMessage(g_hwndDisplay, WM_SETTEXT, 0,
(LPARAM) g_achTemp);
}
break;
}
break;
.
. // additional message processing
.
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
The example in this section demonstrates how to create a dialog box that uses tabs to provide multiple pages of controls. The main dialog box is a modal dialog box. Each page of controls is defined by a dialog box template that has the WS_CHILD style. When a tab is selected, a modeless dialog box is created for the incoming page and the dialog box for the outgoing page is destroyed.
The template for the main dialog box simply defines two button controls.
When processing the
The information is saved in an application-defined structure called DLGHDR.
A pointer to this structure is associated with the dialog box window by using
the
#define C_PAGES 3
typedef struct tag_dlghdr {
HWND hwndTab; // tab control
HWND hwndDisplay; // current child dialog box
RECT rcDisplay; // display rectangle for the tab control
DLGTEMPLATE *apRes[C_PAGES];
} DLGHDR;
The following function processes the WM_INITDIALOG message for the main dialog box. The function allocates the DLGHDR structure, loads the dialog box template resources for the child dialog boxes, and creates the tab control.
The size of each child dialog box is specified by the
VOID WINAPI OnTabbedDialogInit(HWND hwndDlg)
{
DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR));
DWORD dwDlgBase = GetDialogBaseUnits();
int cxMargin = LOWORD(dwDlgBase) / 4;
int cyMargin = HIWORD(dwDlgBase) / 8;
TCITEM tie;
RECT rcTab;
HWND hwndButton;
RECT rcButton;
int i;
// Save a pointer to the DLGHDR structure.
SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr);
// Create the tab control.
InitCommonControls();
pHdr->hwndTab = CreateWindow(
WC_TABCONTROL, "",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, 100, 100,
hwndDlg, NULL, g_hinst, NULL
);
if (pHdr->hwndTab == NULL) {
// handle error
}
// Add a tab for each of the three child dialog boxes.
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = "First";
TabCtrl_InsertItem(pHdr->hwndTab, 0, &tie);
tie.pszText = "Second";
TabCtrl_InsertItem(pHdr->hwndTab, 1, &tie);
tie.pszText = "Third";
TabCtrl_InsertItem(pHdr->hwndTab, 2, &tie);
// Lock the resources for the three child dialog boxes.
pHdr->apRes[0] = DoLockDlgRes(MAKEINTRESOURCE(DLG_FIRST));
pHdr->apRes[1] = DoLockDlgRes(MAKEINTRESOURCE(DLG_SECOND));
pHdr->apRes[2] = DoLockDlgRes(MAKEINTRESOURCE(DLG_THIRD));
// Determine the bounding rectangle for all child dialog boxes.
SetRectEmpty(&rcTab);
for (i = 0; i < C_PAGES; i++) {
if (pHdr->apRes[i]->cx > rcTab.right)
rcTab.right = pHdr->apRes[i]->cx;
if (pHdr->apRes[i]->cy > rcTab.bottom)
rcTab.bottom = pHdr->apRes[i]->cy;
}
rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4;
rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8;
// Calculate how large to make the tab control, so
// the display area can accommodate all the child dialog boxes.
TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab);
OffsetRect(&rcTab, cxMargin - rcTab.left,
cyMargin - rcTab.top);
// Calculate the display rectangle.
CopyRect(&pHdr->rcDisplay, &rcTab);
TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay);
// Set the size and position of the tab control, buttons,
// and dialog box.
SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top,
rcTab.right - rcTab.left, rcTab.bottom - rcTab.top,
SWP_NOZORDER);
// Move the first button below the tab control.
hwndButton = GetDlgItem(hwndDlg, BTN_CLOSE);
SetWindowPos(hwndButton, NULL,
rcTab.left, rcTab.bottom + cyMargin, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
// Determine the size of the button.
GetWindowRect(hwndButton, &rcButton);
rcButton.right -= rcButton.left;
rcButton.bottom -= rcButton.top;
// Move the second button to the right of the first.
hwndButton = GetDlgItem(hwndDlg, BTN_TEST);
SetWindowPos(hwndButton, NULL,
rcTab.left + rcButton.right + cxMargin,
rcTab.bottom + cyMargin, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
// Size the dialog box.
SetWindowPos(hwndDlg, NULL, 0, 0,
rcTab.right + cyMargin +
2 * GetSystemMetrics(SM_CXDLGFRAME),
rcTab.bottom + rcButton.bottom + 2 * cyMargin +
2 * GetSystemMetrics(SM_CYDLGFRAME) +
GetSystemMetrics(SM_CYCAPTION),
SWP_NOMOVE | SWP_NOZORDER);
// Simulate selection of the first item.
OnSelChanged(hwndDlg);
}
// DoLockDlgRes - loads and locks a dialog box template resource.
// Returns the address of the locked resource.
// lpszResName - name of the resource
DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName)
{
HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG);
HGLOBAL hglb = LoadResource(g_hinst, hrsrc);
return (DLGTEMPLATE *) LockResource(hglb);
}
The following function processes the TCN_SELCHANGE notification
message for the main dialog box. The function destroys the dialog box for the
outgoing page, if any. Then it uses the
// OnSelChanged - processes the TCN_SELCHANGE notification.
// hwndDlg - handle to the parent dialog box.
VOID WINAPI OnSelChanged(HWND hwndDlg)
{
DLGHDR *pHdr = (DLGHDR *) GetWindowLong(
hwndDlg, GWL_USERDATA);
int iSel = TabCtrl_GetCurSel(pHdr->hwndTab);
// Destroy the current child dialog box, if any.
if (pHdr->hwndDisplay != NULL)
DestroyWindow(pHdr->hwndDisplay);
// Create the new child dialog box.
pHdr->hwndDisplay = CreateDialogIndirect(g_hinst,
pHdr->apRes[iSel], hwndDlg, ChildDialogProc);
}
The following function processes the WM_INITDIALOG message for each
of the child dialog boxes. You cannot specify the position of a dialog box
created using the CreateDialogIndirect function. This function uses the
// OnChildDialogInit - Positions the child dialog box to fall
// within the display area of the tab control.
VOID WINAPI OnChildDialogInit(HWND hwndDlg)
{
HWND hwndParent = GetParent(hwndDlg);
DLGHDR *pHdr = (DLGHDR *) GetWindowLong(
hwndParent, GWL_USERDATA);
SetWindowPos(hwndDlg, HWND_TOP,
pHdr->rcDisplay.left, pHdr->rcDisplay.top,
0, 0, SWP_NOSIZE);
}
Tab controls in Microsoft® Internet Explorer support the following new features.
| Item States | Tab control items now support an item state to support the |
|---|---|
| Extended Styles | Tab controls now support extended styles that allow the controls to
have enhanced capabilities. See |
| Structures Renamed | All structures used with tab controls have been renamed to conform to current naming conventions, while maintaining backward compatibility. For example, the TCITEM structure is now named TCITEM. |