第41章 オーナードロー・ボタン


第28章でボタンコントロールの使い方をやりました。 今回は、ボタンのオーナードローについてやります。 オーナードローとは、読んで字のごとくオーナーが ドロー(描画する)する事です。 やり方は、簡単です。第28章を参考にボタンを作ってください。 このとき、ウィンドウスタイルはWS_CHILD | WS_VISIBLE | BS_OWNERDRAW にします。 いつ、ボタンに描画するのかというとオーナーのプロシージャで、 WM_DRAWITEMを捕まえたときです。

WM_DRAWITEM idCtl = (UINT) wParam; //コントロールID lpdis = (LPDRAWITEMSTRUCT) lParam; //DRAWITEMSTRUCT構造体へのポインタ

とこうなっています。WM_DRAWITEMメッセージを捕まえたとき wParamとかlParamを調べるとこんな情報がわかります。 ところで、DRAWITEMSTRUCT構造体とはどんなものでしょうか。

typedef struct tagDRAWITEMSTRUCT { // dis UINT CtlType; UINT CtlID; UINT itemID; UINT itemAction; UINT itemState; HWND hwndItem; HDC hDC; RECT rcItem; DWORD itemData; } DRAWITEMSTRUCT;

とこんな風に定義されています。何かを描画したいときは hDCメンバを使えばよいということがわかります。 他のメンバについては、必要が生じたらヘルプで調べてみてください。

では、さっそくサンプルのプログラムを作ってみましょう。 今回は、大きなボタン(100*100)を1つ作って これに、猫のBMPを描画します。そして、このボタンが 押される度に猫の絵が変わるというものです。

// owndr01.cpp #include <windows.h> #define ID_MYOWN 1000 //ボタンのID LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void DrawPic(HDC, HINSTANCE, int); //ボタンに描画 char szClassName[] = "owndr01"; //ウィンドウクラス HWND hMyButton; //オーナードローボタンのウィンドウハンドル int WINAPI 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; }

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

//ウィンドウ・クラスの登録 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 = 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; HDC hdc; static int sw = 1; //どのBMPを描画するか switch (msg) { case WM_CREATE: //親が作られたらすぐにボタンを作る hMyButton = CreateWindow("BUTTON", NULL, //表示文字列 WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 30, 30, //位置 100, 100, //幅・高さ hWnd, //親ウィンドウ (HMENU)ID_MYOWN, //ボタンID (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), //インスタンスハンドル NULL); break; case WM_DRAWITEM: //このメッセージを捕まえたらオーナードローする hdc = ((LPDRAWITEMSTRUCT)lp)->hDC; DrawPic(hdc, (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), sw); break; case WM_COMMAND: switch (LOWORD(wp)) { case ID_MYOWN: if (sw == 1) { sw = 0; return 0L; } if (sw == 0) { sw = 1; return 0L; } break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } 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_CREATEメッセージを捕まえたら、ボタンを作ります。

WM_DRAWITEMを捕まえたら、ボタンのオーナードローをします。 上の例では、hdc = ((LPDRAWITEMSTRUCT)lp)->hDC;で デバイスコンテキストハンドルを取得していますが、 GetDC関数を使っても同じです。このときは忘れずに ReleaseDCしなくてはいけません。

ボタンが押されたら(WM_COMMAND)そのたびに何を 描画するかのスイッチを交互に切り替えています。

void DrawPic(HDC hdc, HINSTANCE hInst, int sw) { HBITMAP hBitmap; HDC hmdc; switch (sw) { case 0: hBitmap = LoadBitmap(hInst, "MYBMP1"); break; case 1: hBitmap = LoadBitmap(hInst, "MYBMP2"); break; } hmdc = CreateCompatibleDC(hdc); SelectObject(hmdc, hBitmap); BitBlt(hdc, 0, 0, 100, 100, hmdc, 0, 0, SRCCOPY); DeleteDC(hmdc); DeleteObject(hBitmap); return; }

swの値によって描画するものを変えています。 ビットマップの描画については 第26章を参照してください。

さて、次にビットマップリソースの作り方をVC++5.0を例にとって 説明します。


リソース関係のツールバーで、左の図でいえば左から5番目の ボタンを押します。もし、このツールバーが表示されていないときは メニューの「ツール」「カスタマイズ」を選択してツールバーの項目で 「リソース」をチェックしておいてください。

これで、新規ビットマップリソースを作成できます。 ビットマッププロパティは、次のようにしておいてください。

ビットマップIDは"MYBMP1"というようにダブルクォーテーションで 囲んでおいてください。幅、高さは100にしておきます。 あらかじめ、希望の画像をクリップボードにコピーしておいて リソース・エジタに貼り付けると作業が簡単です。 「ファイル」「名前を付けて保存」で適当なファイル名をつけて (*.rc)保存します。ソースファイルとともに、「プロジェクト」 「プロジェクトに追加」でプロジェクトに参加させます。 あとは、ビルドボタンを押せば完成です。 なお、リリースバージョンかデバッグバージョンかを選択するには 「ビルド」「アクティブな構成の設定」で決めることができます。 もしくは、「ツール」「カスタマイズ」でビルド関係の ツールバーを出しておけば簡単に設定できます。

ボタン(猫の写真)をクリックすると交互に絵が変わります。(ウィンドウは適当な サイズに縮小してあります) しかし、いろいろ不満があります。クリックに対して反応が悪いときがあります。 プログラムのアルゴリズムを変えてもっとスムースに動くように改良してみてください。


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

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