EternalWindows
タブコントロール / タブコントロールの作成

タブコントロールとは、選択可能なタブを持ったコントロールのことです。

タブコントロールの特徴は、選択したタブによってウインドウの内容が変化するというものです。 これにより、アプリケーションは複数のウインドウを作成することなく複数のページを提供することができます。

タブコントロールにタブを追加するには、TabCtrl_InsertItemを呼び出します。

int TabCtrl_InsertItem(
  HWND hwnd,
  int iItem,
  const LPTCITEM pitem
);

hwndは、タブコントロールのハンドルを指定します。 iItemは、新しく作成されるタブのインデックスを指定します。 pitemは、タブの情報を格納したTCITEM構造体のアドレスを指定します。

TCITEM構造体は、次のように定義されています。

typedef struct tagTCITEM {  
  UINT mask;
#if (_WIN32_IE >= 0x0300)
  DWORD dwState;
  DWORD dwStateMask;
#else
  UINT lpReserved1;
  UINT lpReserved2;
#endif
  LPTSTR pszText;
  int cchTextMax;
  int iImage;
  LPARAM lParam;
} TCITEM, *LPTCITEM;

maskは、どのメンバを初期化するかを表す定数を指定します。 dwStateとdwStateMaskは、タブの状態を表す定数を指定します。 pszTextは、タブの名前を指定します。 cchTextMaxは、pszTextのサイズを指定します。 iImageは、イメージリストのインデックスを指定します。 lParamは、タブに関連付けたいデータを指定します。

タブコントロールで現在、どのタブが選択されているかを特定したい場合はTabCtrl_GetCurSelを呼び出します。

int TabCtrl_GetCurSel(
    HWND hwnd
);

hwndは、タブコントロールのハンドルを指定します。 戻り値は、選択されているタブコントロールのインデックスが返ります

今回は、タブコントロールを作成する例を示しています。 選択されているタブによって、描画される図形が変化します。

#include <windows.h>
#include <commctrl.h>

#pragma comment (lib, "comctl32.lib")

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndTab = NULL;
	
	switch (uMsg) {
		
	case WM_CREATE: {
		TCITEM               item;
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_TAB_CLASSES;
		InitCommonControlsEx(&ic);
		
		hwndTab = CreateWindowEx(0, WC_TABCONTROL, TEXT(""), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);		
		
		item.mask    = TCIF_TEXT;
		item.pszText = TEXT("タブA");
		TabCtrl_InsertItem(hwndTab, 0, &item);
		
		item.mask    = TCIF_TEXT;
		item.pszText = TEXT("タブB");
		TabCtrl_InsertItem(hwndTab, 1, &item);

		return 0;
	}
	
	case WM_PAINT: {
		int         yStart;
		HDC         hdc;
		RECT        rc;
		PAINTSTRUCT ps;

		hdc = BeginPaint(hwnd, &ps);
		
		GetClientRect(hwndTab, &rc);
		yStart = rc.bottom - rc.top;
		
		if (TabCtrl_GetCurSel(hwndTab) == 0)
			Rectangle(hdc, 0, yStart, 30, yStart + 30);
		else
			Ellipse(hdc, 0, yStart, 30, yStart + 30);

		EndPaint(hwnd, &ps);

		return 0;
	}
	
	case WM_NOTIFY:
		if (((LPNMHDR)lParam)->code == TCN_SELCHANGE)
			InvalidateRect(hwnd, NULL, TRUE);
		return 0;
	
	case WM_SIZE:
		MoveWindow(hwndTab, 0, 0, LOWORD(lParam), 30, TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

タブコントロールを作成する場合は、ウインドウクラスにWC_TABCONTROLを指定します。 タブコントロールのサイズはWM_SIZEでMoveWindowを呼び出すことによって決定しています。 タブの追加は、TabCtrl_InsertItemで行います。 TCITEM構造体のmaskにTCIF_TEXTを指定した場合は、 pszTextにタブの名前を指定することができます。

タブが選択された場合は、通知コードがTCN_SELCHANGEであるWM_NOTIFYが送られます。 ここでInvalidateRectを呼び出して再描画を促し、 WM_PAINTでTabCtrl_GetCurSelを呼び出すようにすれば、 選択されたタブによる描画が行われることになります。

case WM_PAINT: {
	int         yStart;
	HDC         hdc;
	RECT        rc;
	PAINTSTRUCT ps;

	hdc = BeginPaint(hwnd, &ps);
	
	GetClientRect(hwndTab, &rc);
	yStart = rc.bottom - rc.top;
	
	if (TabCtrl_GetCurSel(hwndTab) == 0)
		Rectangle(hdc, 0, yStart, 30, yStart + 30);
	else
		Ellipse(hdc, 0, yStart, 30, yStart + 30);

	EndPaint(hwnd, &ps);

	return 0;
}

タブコントロールを表示している場合は、図形の描画位置に気を付けるべきです。 今回の場合、高さ0から30の位置はタブコントロールが占めていますから、 この範囲で描画行っても、図形はタブコントロールに隠れて見えないことになります。 よって、GetClientRectを呼び出してタブコントロールの高さを算出し、 これをy座標の基点として描画を行っています。 nSelIndexの値によって呼び出す関数を変化さしているため、 選択するタブによって描画される図形は変化することになります。


戻る