第86章 ボタンの変形


今回は、自分で作ったボタンを持つツールバーを作ります。
作り方の原理は簡単です。親ウィンドウにstaticウィンドウを 作りツールバーの下地にします。そして、staticウィンドウに ボタンを付けます。いろいろな考え方があると思いますが、手っ取り早いのは ボタンクラスのウィンドウを貼り付けます。そして、オーナードローで ボタンが押されたときとそうでないときのビットマップを表示します。

こう書くとすぐに実現しそうですが、やってみると細かい問題が発生します。 普通に作っていくと、まず、staticウィンドウを作りその子供として ボタンクラスのウィンドウを作るということになります。 ところが、そうするとオーナードローのとき必要となるWM_DRAWITEM メッセージは親ではなく、staticウィンドウに行ってしまいます。 そうなると、staticウィンドウをサブクラス化しないとこのメッセージを 捕まえることができません。これではめんどうです。ボタンも 親の子供として作った方が無難です。

この方針がわかったら後は素直にプログラムを書けば出来上がりです。 ボタンオーナードローについてはすでに 第41章で解説してあるので 参照してください。

// custool1.rdの一部 VC++のリソースエジタを利用する場合は自動的にできるので // それを利用してください。自前で作る人は次のスクリプトを使ってください。 // 自前で作る人はwindows.hとシンボル定義のヘッダーをインクルードします。 ///////////////////////////////////////////////////////////////////////////// // // Bitmap // BTN1UP BITMAP DISCARDABLE "btn1up.bmp" BTN1DOWN BITMAP DISCARDABLE "btn1down.bmp" BTN2UP BITMAP DISCARDABLE "btn2up.bmp" BTN2DOWN BITMAP DISCARDABLE "btn2down.bmp" BTN3UP BITMAP DISCARDABLE "btn3up.bmp" BTN3DOWN BITMAP DISCARDABLE "btn3down.bmp" ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN MENUITEM "三角(&T)", IDM_SANKAKU MENUITEM "丸(&M)", IDM_MARU MENUITEM "逆三角(&R)", IDM_R END

次に実際のBMPは次のようなものです。(24*24ドットに収まるようにしてあります)

btn1up.bmp btn1down.bmp btn2up.bmp btn2down.bmp btn3up.bmp btn3down.bmp
ボタンの陰などを描く関係上ビットマップは256色にした方が よいと思われます。

// custool1.cpp // (C)1997 Y.Kumei #define STRICT #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyBut1Proc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); HWND CreateMyStaticWindow(HWND); HWND CreateMyButton(HWND, int, int, int, int, int); int DrawMyButton(DRAWITEMSTRUCT *, LPSTR); char szClassName[] = "custool1"; //ウィンドウクラス HINSTANCE hInst; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

これは、いつもと同じです。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); 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 = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); }

これもいつもと同じです。

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; 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; static HWND hStatic; static HWND hButton1, hButton2, hButton3; DRAWITEMSTRUCT *lpdraw; switch (msg) { case WM_CREATE: hStatic = CreateMyStaticWindow(hWnd); hButton1 = CreateMyButton(hWnd, 3, 3, 24, 24, IDM_SANKAKU); hButton2 = CreateMyButton(hWnd, 30, 3, 24, 24, IDM_MARU); hButton3 = CreateMyButton(hWnd, 57, 3, 24, 24, IDM_R); break; case WM_DRAWITEM: lpdraw = (DRAWITEMSTRUCT *)lp; id = lpdraw->CtlID; if (lpdraw->itemState & ODS_SELECTED) { switch (id) { case IDM_SANKAKU: DrawMyButton(lpdraw, "BTN1DOWN"); break; case IDM_MARU: DrawMyButton(lpdraw, "BTN2DOWN"); break; case IDM_R: DrawMyButton(lpdraw, "BTN3DOWN"); break; } } else { switch (id) { case IDM_SANKAKU: DrawMyButton(lpdraw, "BTN1UP"); break; case IDM_MARU: DrawMyButton(lpdraw, "BTN2UP"); break; case IDM_R: DrawMyButton(lpdraw, "BTN3UP"); break; } } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_SANKAKU: MessageBox(hWnd, "三角", "IDM_SANKAKU", MB_OK); break; case IDM_MARU: MessageBox(hWnd, "丸", "IDM_MARU", MB_OK); break; case IDM_R: MessageBox(hWnd, "逆三角", "IDM_R", MB_OK); break; } break; case WM_SIZE: MoveWindow(hStatic, 0, 0, LOWORD(lp), 32, TRUE); 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; }

親ウィンドウが作られたらすぐに、staticウィンドウとボタンウィンドウ を作ります。簡単のために自作関数(CreateMyStaticWindow, CreateMyButton関数)を作っておきました。ボタンウィンドウを 作るときにCreateWindow関数の(HMENU)のところは WM_COMMANDメッセージを捕まえたときのIDM_**と同じにしておくと 便利です。(別物にしておくとどうなるか実際実験してみると 面倒くさいプログラムになることがすぐにわかります)

次にWM_DRAWITEMメッセージを捕まえたときの処理ですが いくつかの考え方があります。
まずボタンが押されたかどうかで場合分けをして、 そのなかで、どのID(IDM_**)かによって描画する BMPを選択するか、その逆の場合分けもあると思います。 (どのIDかで場合分けをしてさらにボタンが押されたかどうかで 場合分けする)いずれにしてもここでは、ボタンが押された ことに対するコマンドの中身は記述する必要はありません。 というのも押されるボタンはボタンクラスなので CreateWindow関数の(HMENU)に記述したIDがWM_COMMANDの ところでわかるからです。

WM_COMMANDメッセージを捕まえたら、それぞれやりたいことを 記述します。ここでは簡単のためにどのボタンが(メニュー) 選択されたかをメッセージボックスで表示するだけです。

親のウィンドウサイズが変更されたらstaticウィンドウの サイズも変更する必要があります。ここではMoveWindow関数を 使っています。

あとのメッセージの処理はいつもと同じです。

HWND CreateMyStaticWindow(HWND hWnd) { HWND hStatic; int wx; wx = GetSystemMetrics(SM_CYSCREEN); hStatic = CreateWindow("static", NULL, SS_BLACKRECT | WS_CHILD | WS_VISIBLE, 0, 0, wx, 32, hWnd, (HMENU)100, hInst, NULL); return hStatic; }

スタティクウィンドウを作る関数です。ここではSS_BLACKRECT を使ってウィンドウを黒くしています。

HWND CreateMyButton(HWND hWnd, int x, int y, int w, int h, int ID) { HWND hButton; hButton = CreateWindow( "button", NULL, BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, x, y, w, h, hWnd, (HMENU)ID, hInst, NULL); return hButton; }

ボタンを作る関数です。BS_OWNERDRAWスタイルを忘れずに付けてください。 この関数の最初の引数のhWndは親ウィンドウのハンドルです。

int DrawMyButton(DRAWITEMSTRUCT *lpdraw, LPSTR bmname) { HBITMAP hBmp; BITMAP bitmap_info; HDC hdc, hdc_mem; int wx, wy; hBmp = LoadBitmap(hInst, bmname); GetObject(hBmp, sizeof(BITMAP), &bitmap_info); wx = bitmap_info.bmWidth; wy = bitmap_info.bmHeight; hdc = lpdraw->hDC; hdc_mem = CreateCompatibleDC(hdc); SelectObject(hdc_mem, hBmp); BitBlt(hdc, 0, 0, wx, wy, hdc_mem, 0, 0, SRCCOPY); DeleteDC(hdc_mem); DeleteObject(hBmp); return 0; }

ボタンのBMPを描画する関数です。DRAWITEMSTRUCT構造体 について忘れてしまった人は 第41章を参照してください。 また、簡単のためにビットマップ描画の途中でのエラー処理を 省略しています。各API関数で失敗があったときは、その段階で 適当な数値を返すようにします。

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

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