ツールバー
ツールバーは、ウィンドウの上部、メニューの下部に配置される、ボタン類を配置する棒状のコントロールです。
メニューから項目を選択するよりも素早くクリックできるので、頻繁に使用する機能はツールバーに配置しておくと使いやすいアプリケーションになります。
コモンコントロール
ツールバーはコモンコントロールと呼ばれるものの一種です。
コモンコントロールは初期の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はビットマップに含まれるボタン画像の数を指定します。
LPARAMはTBADDBITMAP
構造体のアドレスを指定します。
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メンバよりも後ろのメンバ(dwData
とiString
)を初期化子で同時に初期化するとき、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
メッセージをツールバーに送信します。
WPARAM、LPARAMは共に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;
}
分かりやすくするためにツールバーを作成する処理を関数に分離しています。
ツールバーのボタンを押すと対応する文字が書かれたメッセージボックスを表示します。
このあたりの処理はボタンコントロールと同じです。
ツールバーのスタイル
ツールバーの作成時、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
スタイルはコモンコントロールで共通して使用できるスタイルです。