EternalWindows
ツールバー / ツールバーの作成

ツールバーとは、ウインドウの上部に表示されるボタンを備えたコントロールのことです。

ツールバーは複数のボタンを持ち、そのボタンを押下することでアプリケーションの機能を利用することができます。 アプリケーションはメニュー項目として機能を公開するだけでも十分だと言えますが、 アクセスをより素早くするためにはツールバーの存在が必要不可欠です。

ツールバーは通常のコントロールと同じようにCreateWindowExで作成することができますが、 CreateToolbarExという専用の関数も用意されています。 この関数は次のように定義されています。

HWND CreateToolbarEx(
  HWND hwnd,
  DWORD ws,
  UINT wID,
  int nBitmaps,
  HINSTANCE hBMInst,
  UINT_PTR wBMID,
  LPCTBBUTTON lpButtons,
  int iNumButtons,
  int dxButton,
  int dyButton,
  int dxBitmap,
  int dyBitmap,
  UINT uStructSize
);

hwndは、親ウインドウのハンドルを指定します。 wsは、ツールバーのウインドウスタイルを指定します。 wIDは、ツールバーのIDを指定します。 nBitmapsは、ビットマップに含まれるボタンの数を指定します。 hBMInstは、ビットマッリソースを格納しているインスタンスハンドルを指定します。 wBMIDは、ビットマッリソースのIDを指定します。 lpButtonsは、ボタン情報を格納するTBBUTTON構造体のアドレスを指定します。 iNumButtonsは、ツールバーに追加するボタンの数を指定します。 1つ目のdxButtonとdxBitmapは、ボタンの幅と高さを指定します。 これらは0でも構いません。 2つ目のdxButtonとdxBitmapは、ボタンイメージの幅と高さを指定します。 これらも0で構いません。 uStructSizeは、TBBUTTON構造体のサイズを指定します。

TBBUTTON構造体は、ツールバーにおける1つのボタンの情報を格納します。 この構造体は次のように定義されています。

typedef struct _TBBUTTON {
    int         iBitmap; 
    int         idCommand; 
    BYTE     fsState; 
    BYTE     fsStyle; 
#ifdef _WIN64
    BYTE     bReserved[6]     // padding for alignment
#elif defined(_WIN32)
    BYTE     bReserved[2]     // padding for alignment
#endif
    DWORD_PTR   dwData; 
    INT_PTR          iString; 
} TBBUTTON, NEAR *PTBBUTTON *LPTBBUTTON;

iBitmapは、ボタンイメージのインデックスを指定します。 I_IMAGENONEを指定した場合は、ボタンイメージを持ちません。 idCommandは、ボタンのIDを指定します。 fsStateは、ボタンの状態を表す定数を指定します。 fsStyleは、ボタンのスタイルを表す定数を指定します。 bReservedは、予約されているため0を指定します。 dwDataは、アプリケーション定義値を指定します。 カスタムドローの際に独自の値を渡したい場合は、このメンバが役に立ちます。 iStringは、ボタンテキストのインデックスを指定します。

今回のプログラムは、ツールバーを作成する例を示しています。 ボタンのイメージは既定のものを使用しています。

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

#define ID_TOOLBAR 100
#define ID_OPEN    200
#define ID_SAVE    300

#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 hwndToolbar = NULL;
	
	switch (uMsg) {
		
	case WM_CREATE: {
		TBBUTTON tbButton[] = {
			{STD_FILEOPEN, ID_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
			{STD_FILESAVE, ID_SAVE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0}
		};
		int                  nCount = sizeof(tbButton) / sizeof(tbButton[0]);
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_BAR_CLASSES;
		InitCommonControlsEx(&ic);
		
		hwndToolbar = CreateToolbarEx(hwnd, WS_CHILD | WS_VISIBLE, ID_TOOLBAR, 0, HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tbButton, nCount, 0, 0, 0, 0, sizeof(TBBUTTON));
		
		return 0;
	}

	case WM_COMMAND:
		if (LOWORD(wParam) == ID_OPEN)
			MessageBox(NULL, TEXT("「開く」ボタンが押されました。"), TEXT("OK"), MB_OK);
		else if (LOWORD(wParam) == ID_SAVE)
			MessageBox(NULL, TEXT("「保存」ボタンが押されました。"), TEXT("OK"), MB_OK);
		else
			;
		return 0;
	
	case WM_SIZE:
		MoveWindow(hwndToolbar, 0, 0, LOWORD(lParam), HIWORD(wParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

ツールバーを作成する場合は、InitCommonControlsExにICC_BAR_CLASSESを指定します。 tbButtonはTBBUTTON構造体の配列であり、3つの要素で構成されています。

TBBUTTON tbButton[] = {
	{STD_FILEOPEN, ID_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
	{0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0}, 0, 0},
	{STD_FILESAVE, ID_SAVE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0}
};

TBBUTTON構造体の最初のメンバはボタンイメージのインデックスですが、 既定のイメージを使用する場合はSTD_FILEOPENのような専用の定数を指定することができます。 この定数を指定した場合は、ファイルを開く形のイメージが表示されることになります。 3番目のメンバはボタンの状態であり、TBSTATE_ENABLEDはボタンが有効であることを意味します。 4番目のメンバはボタンのスタイルであり、BTNS_BUTTONは通常のボタンであることを意味します。 ただし、この定数は0として定義されているため直接0を指定しても問題ありません。 5番目のメンバは予約されている配列であるため、全てのメンバが0で埋まるように{0}を指定しています。 6番目のメンバはアプリケーション定義値ですが、特に使用しないため0を指定します。 7番目のメンバはボタンのテキストのアドレスを指定できますが、今回は使用しないため0を指定しています。

適切に初期化されたTBBUTTON構造体の配列は、CreateToolbarExに指定されることになります。

hwndToolbar = CreateToolbarEx(hwnd, WS_CHILD | WS_VISIBLE, ID_TOOLBAR, 0, 
	(HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tbButton, nCount, 0, 0, 0, 0, sizeof(TBBUTTON));

第2引数はツールバーのウインドウスタイルを指定します。 子ウインドウを示すWS_CHILDと表示状態を示すWS_VISIBLEは必ず指定します。 第5引数は、ボタンに表示するビットマップのリソースを格納するインスタンスハンドルを指定します。 今回は既定のビットマップを表示しますから、HINST_COMMCTRLという既定のハンドルを指定しています。 第6引数はビットマップリソースのIDですが、今回はIDB_STD_SMALL_COLORを指定します。 この定数を指定した場合は、TBBUTTON構造体にSTD_FILEOPENやSTD_FILESAVEを指定できるようになります。 第7引数はTBBUTTON構造体の配列を指定し、第8引数はその要素数を指定します。

ツールバーのボタンが選択された場合は、そのボタンのIDを基にWM_COMMANDが送られます。 今回はボタンが表示されていることを示すメッセージを表示しているだけですが、 実際の開発ではそのボタンに準じた動作を行うことになります。

CreateWindowExによる作成

今回はツールバーをCreateToolbarExで作成しましたが、 CreateWindowExを呼び出すことによって作成することもできます。

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

#define ID_TOOLBAR 100
#define ID_OPEN    200
#define ID_SAVE    300

#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 hwndToolbar = NULL;
	
	switch (uMsg) {
		
	case WM_CREATE: {
		TBBUTTON tbButton[] = {
			{STD_FILEOPEN, ID_OPEN, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
			{STD_FILESAVE, ID_SAVE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0}
		};
		int                  nCount = sizeof(tbButton) / sizeof(tbButton[0]);
		TBADDBITMAP          tbBitmap = {HINST_COMMCTRL, IDB_STD_LARGE_COLOR};
		INITCOMMONCONTROLSEX ic;
		
		ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
		ic.dwICC  = ICC_BAR_CLASSES;
		InitCommonControlsEx(&ic);
		
		hwndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hwnd, (HMENU)ID_TOOLBAR, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		
		SendMessage(hwndToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
		SendMessage(hwndToolbar, TB_ADDBITMAP, nCount, (LPARAM)&tbBitmap);
		SendMessage(hwndToolbar, TB_ADDBUTTONS, nCount, (LPARAM)&tbButton);

		return 0;
	}
	
	case WM_SIZE:
		MoveWindow(hwndToolbar, 0, 0, LOWORD(lParam), HIWORD(wParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

CreateWindowExの第2引数はウインドウクラスの名前であり、 ツールバーの場合はTOOLBARCLASSNAMEを指定します。 CreateWindowExにはボタンなどを指定する引数が含まれていないため、 これらの情報はメッセージで明示的に送信することになります。 ボタンのサイズはTB_BUTTONSTRUCTSIZEで送信し、 使用するリソースIDはTB_ADDBITMAP、 ボタンの情報はTB_ADDBUTTONSで送信します。 ちなみに、ボタンが大きく表示されるのはリソースIDとしてIDB_STD_LARGE_COLORを指定しているからです。



戻る