第61章 ツールバーを作る その1


さて、今回からいよいよ?ツールバーの作り方をやります。 自作のプログラムにメニューバー、ツールバー、ステータスバーが つけばかなり見栄え?がします。最近のユーザーはすっかりGUIに 慣れてしまっていて、見栄えのしないプログラムは使ってくれません。 内容の善し悪しより見栄えが大事な時代です。

ステータスバーと同じようにツールバーにも作り方が大きく分けて 2種類あります。CreateWindowEx関数を使う方法とCreateToolbarEx関数を 使う方法です。今回はCreateToolbarEx関数を使います。まずは、できあがった プログラムを見てみましょう。

4つのボタンがあります。真ん中(「井」と「康」の間)で少し隙間があいていることに注意してください。 また、ボタンに表示されているのはテキストではなくビットマップです。 (紛らわしい)。このビットマップは標準では16*15の大きさですが自分の 好きな大きさに変えても問題はありません。

では、具体的な作り方を説明します。 まず、作るボタンをTBBUTTON構造体に定義します。通常ボタンは複数あるので 構造体を配列にしておきます。

typedef struct _TBBUTTON { //tbb int iBitmap; //ボタンイメージの番号 0から始まる int idCommand; // WM_COMMANDの時のID BYTE fsState; // ボタンの状態 使用可能かなど BYTE fsStyle; // ボタンスタイル DWORD dwData; // アプリケーション定義データ int iString; // ボタンストリング 0から始まる } TBBUTTON, NEAR* PTBBUTTON, FAR* LPTBBUTTON;

この章のサンプルであればfsStateはTBSTATE_ENABLEDになります。 また、fsStyleはTBSTYLE_BUTTONとなります。ボタンは4つありますから

TBBUTTON tbb[] = { {0, IDM_KUME, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {1, IDM_I, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {2, IDM_YASU, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {3, IDM_TAKA, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0} };

とこんな感じになります。2番目のボタンが押されたときは WM_COMMANDのなかでIDM_Iの処理をすればよいということがわかります。

それと、ボタンに表示するビットマップを用意しなくてはいけませんが、 1つ1つのボタンのビットマップを作るのではなく、全部のボタンの ビットマップを横1列に並べてくっつけたようなものを用意します。 今回は1つのビットマップを16*15にしますので、これを 横に4つ並べた大きさのビットマップを作ります。(64*15) この大きさの中にそれぞれ、16*15のビットマップを作るのですが これが結構大変です。そこで、VC++を使っている場合はうまい方法があります。

実は、VC++では、リソースエジタでツールバーを作る場合はMFCを 想定しているようで「ツールバーの新規作成」ボタンを押すと「この機能は MFCでないと使えないよ」というような意味の注意が表示されます。 筆者は、この注意が出たためにしばらくの間VC++ではSDKでツールバーは 使えないのかと早合点していました。しかしそんなことはありません。 ソースコードを書き始めたらすぐにこのボタンを押してください。 そうすると、VC++はSDKでプログラムを書いているということに 気が付かないのか注意が出ません。そして、1つ1つのビットマップを作ってください。 そうすると、全部がくっついた横長のビットマップが自動的にできます。 ただし、ツールバーリソースはSDKでは使いません。ResourceViewからツールバーリソースを 削除しておいてください。(削除しなくても害はありません)そして、リソースエジタが 作ってくれたビットマップ(横長なのでビットマップストリップといいます)を ビットマップリソースとして使います。ただし、この技はソースファイルが*.cの場合は 使えないようです。*.cppにしておいてください。こんな変な技は解説書とかヘルプには 絶対書いてありません。

つぎに、ステータスバーの時と同じように親ウィンドウができたらすぐに ツールバーを作ります。ツールバーはステータスバーと同じコモンコントロールなので commctrl.hをインクルードしたり、comctl32.libをプロジェクトに参加させるのは同じです。 InitCommonControls関数を呼んでおくのも同じです。 その後でCreateToolbarEx関数を実行します。

HWND CreateToolbarEx( HWND hwnd, //親ウィンドウのハンドル DWORD ws, //ウィンドウスタイル UINT wID, //ツールバーのコントロールID int nBitmaps, //hBMInsやwBMIDで特定されたビットマップにはいっているボタンイメージの数 HINSTANCE hBMInst, //ビットマップリソースが入っているモジュールのインスタンスハンドル UINT wBMID, //リソースID LPCTBBUTTON lpButtons,//TBBUTTON構造体の配列のポインタ int iNumButtons, //ツールバーのボタンの数 int dxButton, //ボタンの幅 int dyButton, //ボタンの高さ int dxBitmap, //ボタンイメージの幅 int dyBitmap, //ボタンイメージの高さ UINT uStructSize //TBBUTTON構造体の大きさ );

これを実行すればツールバーが現れます。ボタンの幅、高さは0にしておけば 自動的にちょうどよい大きさになります。

例題のようにボタンに隙間を あけるときは、TB_INSETRBUTTONメッセージを送ります。

TB_INSERTBUTTON wParam = (WPARAM) iButton; lParam = (LPARAM) (LPTBBUTTON) lpButton;

iButtonこのボタン番号の左側にボタンが挿入されます。

lpButton挿入されるボタンの性質を記載したTBBUTTON構造体のポインタ

空白は{0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0}のメンバをもつボタンと考えてください。



さて、前置きがすっかり長くなりましたがサンプルのプログラムを 見てみましょう。その前にビットマップストリップを示します。

これがリソースエジタをだまして作らせたビットマップです。

// tool00.rc #include "windows.h" #include "tool00.h" ///////////////////////////////////////////////////////////////////////////// // // Bitmap // ID_MYBMP BITMAP DISCARDABLE "mybmp.bmp"

今回は、リソーススクリプトも全部自前で作ることにしました。

// tool00.h #define ID_TOOLBAR 100 #define IDM_KUME 101 #define IDM_I 102 #define IDM_YASU 103 #define IDM_TAKA 104 #define ID_MYBMP 105

ヘッダファイルも自前です。いつもは、リソース名は ダブルクオーテーションでくくったものを使用していましたが、 CreateToolbarEx関数の引数の都合上、整数値で表します。 (ID_MYBMP)

// tool00.cpp #define STRICT #include <windows.h> #include <commctrl.h> #include "tool00.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); HWND hToolBar; // ツールバーのウィンドウハンドル char szClassName[] = "tool00"; //ウィンドウクラス TBBUTTON tbb[] = { {0, IDM_KUME, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {1, IDM_I, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {2, IDM_YASU, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {3, IDM_TAKA, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0} }; TBBUTTON tb = {0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0}; int APIENTRY WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!hPrevInst) { if (!InitApp(hCurInst)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

tbbの定義の仕方をよく見ておいてください。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるツールバー",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

毎度おなじみのものです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HINSTANCE hInst; switch (msg) { case WM_CREATE: hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); InitCommonControls(); hToolBar = CreateToolbarEx( hWnd, //親ウィンドウ WS_CHILD | WS_VISIBLE, //ウィンドウスタイル ID_TOOLBAR, // コントロールID 4, //イメージの数 hInst, ID_MYBMP, tbb, 4, // ボタンの数 0, //ボタンの幅 0, //ボタンの高さ 16, //イメージの幅 15, //イメージの高さ sizeof(TBBUTTON)); SendMessage(hToolBar, TB_INSERTBUTTON, 2, (LPARAM)&tb); break; case WM_SIZE: SendMessage(hToolBar, WM_SIZE, wp, lp); break; case WM_COMMAND: switch(LOWORD(wp)) { case IDM_KUME: MessageBox(hWnd, "「粂」ボタンを押したね", "ボタンの反応", MB_OK); break; case IDM_I: MessageBox(hWnd, "「井」ボタンを押したね", "ボタンの反応", MB_OK); break; case IDM_YASU: MessageBox(hWnd, "「康」ボタンを押したね", "ボタンの反応", MB_OK); break; case IDM_TAKA: MessageBox(hWnd, "「孝」ボタンを押したね", "ボタンの反応", MB_OK); break; } break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

ウィンドウのサイズ変更時の処理はステータスバーの時と同じです。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Jul/23/1997 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。