タブコントロール


分類見出し

タブコントロールは、実際のファイル(データファイルではなくて、書類のこと)を管理する時
すぐに必要な情報を参照するためにつける目印、分類見出しタブに似ています

タブコントロールを作成するには CreateWindowEx() 関数を用い
第2引数のクラスで WC_TABCONTROL クラスを指定します
ウィンドウスタイルは、必ず WS_CHILD スタイルの子ウィンドウとして生成し
従来のスタイル以外に、以下のスタイルを指定することもできます

定数解説
TCS_BUTTONS タブをプッシュボタンとして表示する
TCS_FIXEDWIDTH 全てのタブを同じサイズで表示する
TCS_FOCUSNEVER タブは入力フォーカスを受けない
TCS_FOCUSONBUTTONDOWN タブは、選択されと入力フォーカスを受ける
TCS_FORCEICONLEFT タブアイコンを左詰めにし、テキストをセンタリングする
TCS_FORCELABELLEFT アイコンとテキストを左詰にする
TCS_MULTILINE 幅に対し、一行で全てのタブを表示できない場合
タブを改行し、複数行で表示する
TCS_OWNERDRAWFIXED オーナー描画タブである
TCS_RIGHTJUSTIFY 全てのタブの合計の幅が、タブコントロール全体のサイズになるまで拡大される
TCS_SINGLELINE タブが一行で表示される
デフォルトではこのスタイルが用いられる
TCS_TABS タブは、デフォルトスタイルで表示される
TCS_TOOLTIPS ツールヒントコントロールが作成され TTN_NEEDTEXT を発行する

これ以外については、いつもどおり CreateWindowEx() でウィンドウの設定を行います
ただし、この状態では、またタブ自体は生成されていません
タブコントロールは、専用の API は持たず、その操作の多くはマクロを使います
タブコントロールにタブを追加するには TabCtrl_InsertItem マクロを使います

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

hwnd はタブコントロールのハンドルを指定します
iItem には、追加するタブのインデックス番号を指定します
タブは、インデックス番号の昇順に、コントロールの左から右に向かって配置されていきます
pitem は、タブの情報を格納した TCITEM 構造体へのポインタです
マクロが成功すれば、新しいタブのインデックスが、失敗すれば -1 が返ります

TCITEM 構造体は次のように定義されていおり、
タブの情報を取得したり、設定したりする時に用います
(TCITEM 構造体は TC_ITEM とも定義されいますが、両方とも同じ構造体です)
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, FAR *LPTCITEM;
mask メンバには、取得、または設定するメンバを表すフラグを指定します
この値は、以下の定数を組み合わせて指定することができます

定数解説
TCIF_ALL 全てのメンバが有効
TCIF_IMAGE image メンバが有効
TCIF_PARAM iParam メンバが有効
TCIF_RTLREADING タブのテキストを右から左へ表示する
ヘブライ語、またはアラビア語で有効
TCIF_TEXT pszText

lpReserved1 と lpReserved2 メンバは、予約済みのメンバなので、使いません
pszText は、情報を取得する時は、文字列を受けるバッファへのポインタを
設定する場合は、タブの文字列を指定します

cchTextMax には、pszText メンバのバッファサイズを指定します
タブの情報を取得する時にのみ使用するメンバで、それ以外は無視されます

image は、タブコントロールに設定するイメージリストのインデックスです
イメージリストもコモンコントロールの一つで、後ほど詳しく解説します
情報を取得した時、このメンバが -1 であれば、タブにイメージがないことを意味します
lParam は、タブに関連付けられた追加情報を指定できます

さて、これで TabCtrl_InsertItem() マクロを用いてタブを追加できます
タブコントロールには、必要な数だけ自由にタブを追加することができます
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static HWND hTab;
	static TCITEM tc_item;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();
		hTab = CreateWindowEx( 0 , WC_TABCONTROL , NULL ,
			WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE ,
			0 , 0 , 10 , 10 , hWnd , (HMENU)0x10 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		tc_item.mask = TCIF_TEXT;
		tc_item.pszText = TITLE;
		TabCtrl_InsertItem(hTab , 0 , &tc_item);

		tc_item.pszText = TEXT("Tokyo mew mew");
		TabCtrl_InsertItem(hTab , 1 , &tc_item);

		return 0;
	case WM_SIZE:
		MoveWindow(hTab , 0 , 0 , LOWORD(lp) , 30 , TRUE);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムは、上の図のように二つのタブを持つタブコントロールを作成します
タブをクリックすれば、そのタブが他のタブよりも前に浮き出て強調されます

上のプログラムでは、タブを表示するだけで、タブをクリックしても何もおきません
しかし、何らかのタブがクリックされた場合、それに対応した処理が必要でしょう

タブがクリックされると、親ウィンドウには WM_NOTIFY が送信されます
このメッセージは、コントロール内で何らかのイベントが発生した時や
コントロールが何らかの情報を必要としている時に、親ウィンドウに送信されます

WPARAM には、このメッセージを送信したこコントロールの ID が格納されています
LPARAM には、メッセージの情報を格納した NMHDR 構造体へのポインタが格納されています
このメッセージの戻り値は、通知コードによって異なりますが、通常は無視されます

NMHDR 構造体は次のように定義されています
typedef struct tagNMHDR { 
    HWND hwndFrom; 
    UINT idFrom; 
    UINT code; 
} NMHDR;
hwndFrom には、メッセージを送信するコントロールのハンドルを、
idFrom は、メッセージを送信するコントロールの ID を指定します
code には、送信するコントロールが定める固有の通知コードを指定します

WM_NOTIFY が TCN_SELCHANGE 通知コードを受け取った場合
タブコントロールのタブが選択されたことを意味し、このタイミングで処理を行います

ただし、プログラムはどのタブが選択されたのかを知る必要があります
これは、選択されているタブのインデックスを取得することで解決することができます
現在選択されているタブを取得するには TabCtrl_GetCurSel() マクロを使います

int TabCtrl_GetCurSel(HWND hwnd);

hwnd に、タブコントロールのウィンドウハンドルを指定します
このマクロは、指定したタブコントロールで選択されているタブのインデックスを返します

WM_NOTIFY メッセージを受け取り、通知コードが TCN_SELCHANGE だった場合
TabCtrl_GetCurSel() マクロを用いて選択されたタブを取得すれば
タブが選択されるごとに、何らかの処理を行うことが可能になります
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")
#define TARUTO TEXT("Magical nyan nyan TARUTO")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	TCITEM tc_item;
	static PTSTR pstr;
	static HWND hTab;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();
		hTab = CreateWindowEx( 0 , WC_TABCONTROL , NULL ,
			WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE ,
			0 , 0 , 10 , 10 , hWnd , (HMENU)0x10 ,
			((LPCREATESTRUCT)lp)->hInstance , NULL
		);
		tc_item.mask = TCIF_TEXT;
		tc_item.pszText = TITLE;
		TabCtrl_InsertItem(hTab , 0 , &tc_item);

		tc_item.pszText = TARUTO;
		TabCtrl_InsertItem(hTab , 1 , &tc_item);

		return 0;
	case WM_SIZE:
		MoveWindow(hTab , 0 , 0 , LOWORD(lp) , 30 , TRUE);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);
		TextOut(hdc , 0 , 30 , pstr , lstrlen(pstr));
		EndPaint(hWnd , &ps);
		return 0;
	case WM_NOTIFY:
		switch (((NMHDR *)lp)->code) {
		case TCN_SELCHANGE:
			if (TabCtrl_GetCurSel(hTab) == 0) pstr = TITLE;
			else pstr = TARUTO;
			break;
		}
		InvalidateRect(hWnd , NULL , TRUE);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムは、先ほどのプログラムと同様に2つのタブを生成します
タブを選択すると、タブごとに割り当てた適切な文字列がクライアント領域に描画されます


TabCtrl_InsertItem()

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

タブコントロールに新しいタブを追加します

hwnd - タブコントロールのハンドルを指定します
iItem - 追加するタブのインデックス番号を指定します
pitem - タブの情報を格納した TCITEM 構造体へのポインタです

戻り値 - 新しいタブのインデックス、失敗すれば -1

TabCtrl_GetCurSel()

int TabCtrl_GetCurSel(HWND hwnd);

選択されているタブのインデックス番号を返します

hwnd - タブコントロールのウィンドウハンドルを指定します

戻り値 - 選択されているタブのインデックス



前のページへ戻る次のページへ