ツールバー

ツールバーは、ウィンドウの上部、メニューの下部に配置される、ボタン類を配置する棒状のコントロールです。
メニューから項目を選択するよりも素早くクリックできるので、頻繁に使用する機能はツールバーに配置しておくと使いやすいアプリケーションになります。
ツールバーのサンプル

コモンコントロール

ツールバーはコモンコントロールと呼ばれるものの一種です。
コモンコントロールは初期のWindowsには存在せず、後から追加されたGUI機能です。
(といってもかなり古い話です)
使用するにはcommctrl.hをインクルードし、Comctl32.libをリンクする必要があります。
また、実際にコモンコントロールを作成する前にInitCommonControls関数を実行する必要があります。


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

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

//途中省略

InitCommonControls();
void InitCommonControls();
コモンコントロールを初期化し使用可能にする。

実行のタイミングはコモンコントロール作成前であればどこでも構いませんが、メインのウィンドウプロシージャのWM_CREATEメッセージ内が無難でしょう。

InitCommonControlsEx関数

最近の環境向け(Windows2000やXP以降)ならばInitCommonControlsEx関数の使用が推奨されます。

BOOL InitCommonControlsEx(
 const INITCOMMONCONTROLSEX *picce
);
INITCOMMONCONTROLSEX構造体で指定されるコモンコントロールを初期化し使用可能にする。
成功した場合はTRUEを、失敗した場合はFALSEを返す。

InitCommonControlsEx関数は、使用するコモンコントロールを個別に指定して必要なものだけを使用可能にします。
引数にはINITCOMMONCONTROLSEX構造体のポインタを指定します。

typedef struct tagINITCOMMONCONTROLSEX {
 DWORD dwSize;
 DWORD dwICC;
} INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;
コモンコントロールを初期化する情報を扱う構造体。

dwSizeメンバはこの構造体のサイズです。
つまりsizeof(INITCOMMONCONTROLSEX)を指定します。

dwICCメンバは初期化するコモンコントロールの定数の組み合わせです。
たくさん種類があるのですが、ここではツールバーコントロールを含むICC_BAR_CLASSESを指定します。


INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
icc.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&icc);

ツールバーの作成

ツールバーの作成方法はいくつかありますが、最も基本的な方法を説明します。
手順はおおまかに

  • ボタン上に表示する画像の用意
  • ツールバーをCreateWindowEx関数で作成
  • ツールバーボタンのサイズを設定(任意)
  • ツールバーに画像を登録
  • ツールバーにボタンを登録
  • ウィンドウサイズ変更時にツールバーサイズも調整されるようにする

という流れになります。

画像の用意

ツールバーを作成する前に、ツールバーのボタンに表示するビットマップ画像を用意します。
ここでは以下の画像を使用します。
ツールバーのボタンに使用する画像

「あいうえお」と書かれた横長の画像です。
一文字当たりのサイズは「16 × 15」で作成しています。
(つまり画像サイズは「80 × 15」。ただしこのページ上では倍に拡大して表示しています)
今回は「あ」「い」「う」「え」「お」という五つのボタンを作成しますが、画像を五つ用意するのではなく、ひとつの画像から必要な箇所を読み取ることで複数のボタンを作成します。

プロジェクトにビットマップを追加する方法はリソースファイル の項を参照してください。
ここではビットマップリソースの識別子にIDB_BITMAP1を使用します。

なお、画像の色数は256色(8bitカラー)以下で作成してください。

ツールバーウィンドウの作成

ツールバーはCreateWindowEx関数でメインウィンドウの子ウィンドウとして作成します。


HWND hToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
	WS_CHILD | WS_VISIBLE,
	0, 0, 0, 0,
	hWnd, NULL, hInst, NULL);

ウィンドウクラスにはTOOLBARCLASSNAMEという定数を指定します。
これがツールバーのウィンドウクラスです。
中身は"ToolbarWindow32"という文字列なので、これを指定することもできます。

ウィンドウスタイルはWS_CHILDが必須です。
WS_VISIBLEスタイルを指定しないと表示されないので、これも指定しておきます。
(作成後にShowWindow関数で表示状態を変更することもできます)

位置やサイズはすべて0でかまいません。

ツールバーはCreateToolbarEx関数でも作成可能です。
しかしこれで作成したツールバーはサポートされない機能があるため非推奨です。

TB_BUTTONSTRUCTSIZEメッセージ

ツールバーを作成したら、まず最初にTB_BUTTONSTRUCTSIZEメッセージをツールバーに送信する必要があります。
これはツールバーのボタン情報を格納するTBBUTTON構造体のサイズをツールバーに設定します。

WPARAMにはTBBUTTON構造体のサイズを指定します。
LPARAMには0を指定します。


SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);

このメッセージの送信はツールバー作成時の"決まり"で、パラメータを変更することはありません。
TBBUTTON構造体については後述します。

TB_SETBITMAPSIZEメッセージ

次にTB_SETBITMAPSIZEメッセージを送信します。
これはボタンに使用するビットマップ(画像)のサイズの指定です。
(ボタンひとつのサイズ)

WPARAMには0を指定します。
LPARAMの下位ワードは画像の横幅、上位ワードは画像の縦幅を指定します。


SendMessage(hToolbar, TB_SETBITMAPSIZE, 0, MAKELPARAM(16, 15));

上記コードでは「16 × 15」のサイズを指定しています。
これはデフォルトの値で、何も指定しなければ(このメッセージを送信しなければ)このサイズが使用されます。
下位ワードと上位ワードにそれぞれ値を指定するにはMAKELPARAMマクロを使用するのが便利です。

TB_ADDBITMAPメッセージ

次にTB_ADDBITMAPメッセージでツールバーに使用するビットマップを登録します。

WPARAMはビットマップに含まれるボタン画像の数を指定します。
LPARAMTBADDBITMAP構造体のアドレスを指定します。

TBADDBITMAP構造体はツールバーボタンの画像を管理する構造体です。

typedef struct tagTBADDBITMAP {
 HINSTANCE hInst;
 UINT_PTR nID;
} TBADDBITMAP, *LPTBADDBITMAP;
ツールバーのボタンに追加する画像の情報を格納する構造体。

hInstメンバは、ビットマップリソースを含むモジュールのインスタンスの指定です。
NULLを指定することも可能です。

nIDメンバは、hInstメンバがNULLでない場合はビットマップリソースの識別子です。
hInstメンバがNULLの場合はビットマップハンドルです。


TBADDBITMAP tb;
tb.hInst = NULL;
tb.nID = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));

SendMessage(hToolbar, TB_ADDBITMAP, (WPARAM)5, (LPARAM)&tb);

今回の画像中のボタンの数は5個なので、WPARAMには5を指定します。

なお、今回は使用しませんが、このメッセージのSendMessage関数の戻り値は追加した画像の先頭のボタン画像のインデックスです。
今回ならば「あ」の画像の番号です。
登録に失敗した場合は「-1」を返します。

TB_ADDBUTTONSメッセージ

最後にTB_ADDBUTTONSメッセージでツールバーにボタンを追加します。

WPARAMは追加するボタンの数を指定します。
LPARAMは追加するボタンの情報を格納するTBBUTTON構造体の配列のアドレスを指定します。

TBBUTTON構造体はツールバーボタンの情報を管理する構造体です。

typedef struct _TBBUTTON {
 int iBitmap;
 int idCommand;
 BYTE fsState;
 BYTE fsStyle;
#if ...
 BYTE bReserved[6];
#else
 BYTE bReserved[2];
#endif
 DWORD_PTR dwData;
 INT_PTR iString;
} TBBUTTON, *PTBBUTTON, *LPTBBUTTON;
ツールバーのボタンに追加するボタンの情報を格納する構造体。

iBitmapメンバは、ボタン画像の番号の指定です。
0を指定すると最初の画像(今回は「あ」)が指定されます。
1だと二番目の画像(今回は「い」)が指定されます。
ボタンの種類にセパレーターを指定した場合、セパレーターの幅のピクセル単位での指定となります。

idCommandメンバは、ボタンの識別子です。
ここに指定した値がWM_COMMANDメッセージに送られてきます。
(WPARAMの下位ワード)

fsStateメンバはボタンの状態を示すフラグです。
以下の定数の組み合わせを指定できます。

定数 説明
TBSTATE_CHECKED チェックボックススタイルを持つボタンで、チェックされている状態
TBSTATE_ELLIPSES ボタンテキストの省略
TBSTATE_ENABLED 有効なボタン
TBSTATE_HIDDEN ボタンの非表示
TBSTATE_INDETERMINATE ボタンの灰色表示
TBSTATE_MARKED ボタンがマークされた状態
この状態の意味はアプリケーションにより異なる
TBSTATE_PRESSED ボタンは押されている状態
TBSTATE_WRAP ボタンの後ろに改行を入れる
TBSTATE_ENABLEDを同時に指定する必要がある

通常のボタンを作成する場合はTBSTATE_ENABLEDを指定します。

fsStyleメンバはボタンのスタイルの指定です。
以下の定数の組み合わせを指定します。
同じ意味の定数が二つずつありますが、「TBSTYLE_○○」は古い環境に対応する場合に使用します。

定数 説明
TBSTYLE_BUTTON
BTNS_BUTTON
標準ボタン
TBSTYLE_CHECK
BTNS_CHECK
トグルボタン
「押された状態」と「押されていない状態」の二つの状態が切り替わるボタン
TBSTYLE_CHECKGROUP
BTNS_CHECKGROUP
ラジオボタン
TBSTYLE_GROUP
BTNS_GROUP
TBSTYLE_CHECK(BTNS_CHECK)と組み合わせることでラジオボタンが作成される
TBSTYLE_DROPDOWN
BTNS_DROPDOWN
ドロップダウンリストを持つボタン
ボタンを押すとWM_COMMANDメッセージではなくTBN_DROPDOWN通知コードが送られる
TBSTYLE_NOPREFIX
BTNS_NOPREFIX
ボタン文字列の&記号(アンパサンド)を関連付けられているアクセラレータのプレフィックス文字と解釈しない
TBSTYLE_SEP
BTNS_SEP
セパレータ
これ自体はボタンではなく、ボタンとボタンの間にスペースを作るために使用する
TBSTYLE_AUTOSIZE
BTNS_AUTOSIZE
ボタンの幅を、ボタン画像とテキストにより自動調節する

bReservedメンバは使用しません。
初期化子を使用する場合は{0}を指定します。

dwDataメンバは、ボタンに関連付ける任意の値です。
使用しない場合は0を指定します。

iStringメンバは、ボタンに表示する文字列のポインタ、またはツールバーに登録している文字列のインデックスを指定します。


#define IDC_A 101
#define IDC_I 102
#define IDC_U 103
#define IDC_E 104
#define IDC_O 105

TBBUTTON tbb[] = {
	{ 0, IDC_A, TBSTATE_ENABLED, BTNS_BUTTON },
	{ 1, IDC_I, TBSTATE_ENABLED, BTNS_BUTTON },
	{ 0, 0, 0, BTNS_SEP },
	{ 2, IDC_U, TBSTATE_ENABLED, BTNS_BUTTON },
	{ 3, IDC_E, TBSTATE_ENABLED, BTNS_BUTTON },
	{ 4, IDC_O, TBSTATE_ENABLED, BTNS_BUTTON }
};

SendMessage(hToolbar, TB_ADDBUTTONS, (WPARAM)6, (LPARAM)&tbb);

fsStyleメンバまでは値を指定し、残りのメンバは0で初期化します。
(構造体の初期化子が足りない場合、残りは0で埋められる)

今回は「あい」と「うえお」の間にセパレーターをひとつ挿入し、全部で6つのボタンを追加します。

bReservedメンバはコード上からは使用することはありませんが、配列で6バイトまたは2バイトの領域(環境による)を持っています。
bReservedメンバよりも後ろのメンバ(dwDataiString)を初期化子で同時に初期化するとき、bReservedメンバは0ではなく{0}で初期化する必要があります。
bReservedメンバは配列であるため、0で初期化するとその0は配列の最初の要素の値となり、次の要素はdwDataメンバの値が使用されます。
つまりデータが前にズレてしまいます。
{0}で初期化するとbReservedメンバの配列要素はすべて0で埋められるので、このような問題は起こりません


TBBUTTON tbb[] = {
	{ 0, IDC_A, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 1, L"あ"},
	{ 1, IDC_I, TBSTATE_ENABLED, BTNS_BUTTON, 0, 2, L"い" }
};

//上の初期化方法は以下と同じ
//(bReservedメンバの配列の要素数は2とする)
tbb[0].iBitmap		= 0;
tbb[0].idCommand	= IDC_A;
tbb[0].fsState		= TBSTATE_ENABLED;
tbb[0].fsStyle		= BTNS_BUTTON;
tbb[0].bReserved	= {0, 0}; //足りない要素は0で埋められる
tbb[0].dwData		= 1;
tbb[0].iString		= L"あ";

tbb[1].iBitmap		= 1;
tbb[1].idCommand	= IDC_I;
tbb[1].fsState		= TBSTATE_ENABLED;
tbb[1].fsStyle		= BTNS_BUTTON;
tbb[1].bReserved	= {0, 2}; //メモリ上で連続している次のデータが使用される
tbb[1].dwData		= L"い";
tbb[1].iString		= 0; //初期化子が足りないため0で埋められる

TB_AUTOSIZEメッセージ

ここまでの手順でウィンドウにツールバーが追加されます。
しかしこの状態では、ウィンドウのサイズを変更してもツールバーのサイズは変更されません。
これを解決するために親ウィンドウのWM_SIZEメッセージ内でTB_AUTOSIZEメッセージをツールバーに送信します。

WPARAMLPARAMは共に0を指定します。
これで親ウィンドウのサイズ変更時にツールバーサイズも調整されます。


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND hToolbar;

	switch (message)
	{
	case WM_SIZE:
		SendMessage(hToolbar, TB_AUTOSIZE, 0, 0);
		break;

全体のサンプルコード

今までの手順全体のサンプルコードです。


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

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

//ウィンドウの生成等は省略

#define IDC_A 101
#define IDC_I 102
#define IDC_U 103
#define IDC_E 104
#define IDC_O 105

//ツールバーを作成する
HWND CreateToolbar(HWND hWnd)
{
	//コモンコントロールの初期化
	INITCOMMONCONTROLSEX icc;
	icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
	icc.dwICC = ICC_BAR_CLASSES;
	InitCommonControlsEx(&icc);

	//ツールバーの作成
	HWND hToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
		WS_CHILD | WS_VISIBLE,
		0, 0, 0, 0,
		hWnd, NULL, hInst, NULL);

	//ツールバーを初期化
	SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
	SendMessage(hToolbar, TB_SETBITMAPSIZE, 0, MAKELPARAM(16, 15));

	//ビットマップの登録
	TBADDBITMAP tb;
	tb.hInst = NULL;
	tb.nID = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
	SendMessage(hToolbar, TB_ADDBITMAP, (WPARAM)5, (LPARAM)&tb);

	//ボタンの定義と登録
	TBBUTTON tbb[] = {
		{ 0, IDC_A, TBSTATE_ENABLED, BTNS_BUTTON },
		{ 1, IDC_I, TBSTATE_ENABLED, BTNS_BUTTON },
		{ 0, 0, 0, BTNS_SEP },
		{ 2, IDC_U, TBSTATE_ENABLED, BTNS_BUTTON },
		{ 3, IDC_E, TBSTATE_ENABLED, BTNS_BUTTON },
		{ 4, IDC_O, TBSTATE_ENABLED, BTNS_BUTTON }
	};
	SendMessage(hToolbar, TB_ADDBUTTONS, (WPARAM)6, (LPARAM)&tbb);

	return hToolbar;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND hToolbar;

	switch (message)
	{
	case WM_CREATE: //ウィンドウの作成
		hToolbar = CreateToolbar(hWnd);
		break;

	case WM_SIZE: //ウィンドウサイズ変更
		SendMessage(hToolbar, TB_AUTOSIZE, 0, 0);
		break;

	case WM_COMMAND: //コントロールの操作
		switch (LOWORD(wParam))
		{
		case IDC_A:
			MessageBox(hWnd, L"あ", L"情報", MB_OK);
			break;
		case IDC_I:
			MessageBox(hWnd, L"い", L"情報", MB_OK);
			break;
		case IDC_U:
			MessageBox(hWnd, L"う", L"情報", MB_OK);
			break;
		case IDC_E:
			MessageBox(hWnd, L"え", L"情報", MB_OK);
			break;
		case IDC_O:
			MessageBox(hWnd, L"お", L"情報", MB_OK);
			break;
		}
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

ツールバーの実装サンプル1

分かりやすくするためにツールバーを作成する処理を関数に分離しています。

ツールバーのボタンを押すと対応する文字が書かれたメッセージボックスを表示します。
このあたりの処理はボタンコントロールと同じです。

ツールバーのスタイル

ツールバーの作成時、CreateWindow(Ex)関数に以下の定数の組み合わせを指定することでツールバーのスタイルや動作を変更できます。

定数 説明
TBSTYLE_TOOLTIPS ツールチップ(ツールヒントコントロール)の作成
(ボタン上にカーソルを合わせるとポップアップする説明テキストのこと)
TBSTYLE_WRAPABLE 複数行ツールバー
TBSTYLE_ALTDRAG CCS_ADJUSTABLEスタイルを持つツールバーで、ALTキーを押しながらドラッグすることでツールバーボタンの位置を変更可能にする
TBSTYLE_FLAT フラットツールバー
ツールバーの表示前に設定しておく必要がある
TBSTYLE_LIST 画像の右側にテキストを表示する
ツールバーの表示前に設定しておく必要がある
TBSTYLE_CUSTOMERASE WM_ERASEBKGNDメッセージの処理時にNM_CUSTOMDRAW通知コードを生成する
TBSTYLE_REGISTERDROP カーソルがボタン上を通過するときにTBN_GETOBJECT通知コードを生成する
TBSTYLE_TRANSPARENT 透明ツールバー
ツールバーの背景が透明になる
(ボタンは透明にはならない)
ツールバーの表示前に設定しておく必要がある

CCS_ADJUSTABLEスタイルはコモンコントロールで共通して使用できるスタイルです。