こう書くとすぐに実現しそうですが、やってみると細かい問題が発生します。 普通に作っていくと、まず、staticウィンドウを作りその子供として ボタンクラスのウィンドウを作るということになります。 ところが、そうするとオーナードローのとき必要となるWM_DRAWITEM メッセージは親ではなく、staticウィンドウに行ってしまいます。 そうなると、staticウィンドウをサブクラス化しないとこのメッセージを 捕まえることができません。これではめんどうです。ボタンも 親の子供として作った方が無難です。
この方針がわかったら後は素直にプログラムを書けば出来上がりです。 ボタンオーナードローについてはすでに 第41章で解説してあるので 参照してください。
次に実際のBMPは次のようなものです。(24*24ドットに収まるようにしてあります)// 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
btn1up.bmp | btn1down.bmp | btn2up.bmp | btn2down.bmp | btn3up.bmp | btn3down.bmp |
これは、いつもと同じです。// 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; }
親ウィンドウが作られたらすぐに、staticウィンドウとボタンウィンドウ を作ります。簡単のために自作関数(CreateMyStaticWindow, CreateMyButton関数)を作っておきました。ボタンウィンドウを 作るときにCreateWindow関数の(HMENU)のところは WM_COMMANDメッセージを捕まえたときのIDM_**と同じにしておくと 便利です。(別物にしておくとどうなるか実際実験してみると 面倒くさいプログラムになることがすぐにわかります)//ウィンドウプロシージャ 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; }
次にWM_DRAWITEMメッセージを捕まえたときの処理ですが
いくつかの考え方があります。
まずボタンが押されたかどうかで場合分けをして、
そのなかで、どのID(IDM_**)かによって描画する
BMPを選択するか、その逆の場合分けもあると思います。
(どのIDかで場合分けをしてさらにボタンが押されたかどうかで
場合分けする)いずれにしてもここでは、ボタンが押された
ことに対するコマンドの中身は記述する必要はありません。
というのも押されるボタンはボタンクラスなので
CreateWindow関数の(HMENU)に記述したIDがWM_COMMANDの
ところでわかるからです。
WM_COMMANDメッセージを捕まえたら、それぞれやりたいことを 記述します。ここでは簡単のためにどのボタンが(メニュー) 選択されたかをメッセージボックスで表示するだけです。
親のウィンドウサイズが変更されたらstaticウィンドウの サイズも変更する必要があります。ここではMoveWindow関数を 使っています。
あとのメッセージの処理はいつもと同じです。
スタティクウィンドウを作る関数です。ここではSS_BLACKRECT を使ってウィンドウを黒くしています。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; }
ボタンを作る関数です。BS_OWNERDRAWスタイルを忘れずに付けてください。 この関数の最初の引数のhWndは親ウィンドウのハンドルです。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; }
ボタンのBMPを描画する関数です。DRAWITEMSTRUCT構造体 について忘れてしまった人は 第41章を参照してください。 また、簡単のためにビットマップ描画の途中でのエラー処理を 省略しています。各API関数で失敗があったときは、その段階で 適当な数値を返すようにします。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; }
Update Nov/04/1997 By Y.Kumei