第50章 子供ウィンドウを作る


さて、簡単なアニメーションを実現するには前章で解説した メタファイルを使うほかにもっと別な方法が考えられます。 小さな子供ウィンドウを作ってこれに動かしたいものを描画します。 そして、子供ウィンドウごと移動してやればよいですね。 これが意外にスムースに動きます。今回は、この下準備として 子供ウィンドウの作り方について解説します。

実は、子供ウィンドウは、すでに何回も作っています。 第28章では、ボタンを子供ウィンドウとして 作っています。関数はCreateWindow関数を使いました。ウィンドウクラスは あらかじめ定義されているBUTTONクラスを使いました。 一般のウィンドウでは、ウィンドウクラスを定義してやらなくてはいけません。 必要になったらその場で定義してもいいのですが、長ったらしくなって 見た目もよくないです。そこで、いつも使っているInitApp関数を 少し改造して親ウィンドウのクラス登録だけでなく、子供ウィンドウの クラス登録にも使えるようにしておくと便利です。

背景ブラシや、アイコン、カーソルなどが親と同じでよいとすれば、 プロシージャとクラス名を子供独自のものにします。 InitApp関数の引数にプロシージャ名とクラス名を追加で増やせばよいですね。

では、さっそくサンプルのプログラムを見てみましょう。

// chd01.cpp #define STRICT #include <windows.h> #define ID_MYCHILD 100 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK ChildProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE, WNDPROC, LPCTSTR); BOOL InitInstance(HINSTANCE, int, LPCTSTR); int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; LPCTSTR szClassName = "chd01"; //ウィンドウクラス if (!hPrevInst) { if (!InitApp(hCurInst, WndProc, szClassName)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow, szClassName)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }

子供ウィンドウのプロシージャ関数のプロトタイプ宣言が増えています。 また、親ウィンドウのクラス名をWinMain関数内で宣言してみました。 従ってこの名前をInitInstance関数内にも知らせてやる必要があるので この関数の引数も一つ増えています。しかし、基本的にはいつもと同じです。 子供ウィンドウのIDを定義しておきます。(ID_MYCHILD)

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst, WNDPROC WndProc, LPCTSTR szClassName) { 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, LPCTSTR szClassName) { 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; }

これも、いつもと同じですね。引数が1つ増えている以外は。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HWND hChdWnd; HINSTANCE hInst; switch (msg) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lp)->hInstance; InitApp(hInst, ChildProc, "child"); hChdWnd = CreateWindow("child", "子供ウィンドウ",//タイトルバーにこの名前が表示されます WS_CHILD | WS_SYSMENU | WS_THICKFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 200, //幅 100, //高さ hWnd, //親ウィンドウのハンドル、親を作るときはNULL (HMENU)ID_MYCHILD, //メニューハンドル、子供のID hInst, //インスタンスハンドル NULL); ShowWindow(hChdWnd, SW_SHOW); UpdateWindow(hChdWnd); 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; }

さて、親ウィンドウのプロシージャです。ここでは親ウィンドウが 作られるとすぐに子供ウィンドウを作っています。 このときInitApp(hInst, ChildProc, "child");というように InitApp関数を再利用(?)できます。CreateWindow関数についてはあまり説明は 必要ないですね。ウィンドウクラスは先ほど定義した"child"にします。

ウィンドウスタイルはWS_CHILDで子供ウィンドウを表します。 WS_SYSMENU はシステムメニューを持つという意味です。 WS_THICKFRAMEはウィンドウの大きさを変えれられるように太いフレームを 持つという意味です。 WS_CAPTION はタイトルバーを持つという意味です。 WS_MINIMIZEBOX 、 WS_MAXIMIZEBOXはそれぞれ最小化ボタン、最大化ボタンを 持つという意味です。

また、CreateWindow関数の9番目の引数(HMENU)は子供を作るときは 子供ウィンドウのIDを指定します。(子供ウィンドウはメニューを持てない)

LRESULT CALLBACK ChildProc(HWND hChdWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; switch (msg) { case WM_CLOSE: id = MessageBox(hChdWnd, "子供ウィンドウを閉じてもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) DestroyWindow(hChdWnd); break; default: return (DefWindowProc(hChdWnd, msg, wp, lp)); } return 0L; }

子供ウィンドウのプロシージャです。 子供ウィンドウで右上のバッテン印や、システムメニューの「閉じる」 が押されたときは子供ウィンドウをDestroyWindow関数で破棄しています。

子供ウィンドウが存在しているときに親ウィンドウを閉じる(アプリケーションを 終了する)時はどうでしょうか。親ウィンドウのプロシージャを見ても、 子供ウィンドウを破棄する記述はどこにもありません。しかし、ボタンを作ったときと同じように 親ウィンドウが破棄されるときは子供ウィンドウは自動的に破棄されるので DestroyWindow関数を呼ぶ必要はありません。

また、子供と親のプロシージャを共通にしたらどうなるでしょうか。 場合によっては問題が起こります。上の例では子供ウィンドウを閉じると親も一緒に 閉じてしまいます。場合によっては全く問題がないこともあります。プログラムの内容で どうするか決めて下さい。

では、実行してみましょう。

この子どもウィンドウにはシステムメニューやら最小化ボタン、最大化ボタン 、ウィンドウサイズ変更のための太い枠などを持った立派な?ウィンドウです。

子供ウィンドウは親の外には出られません。


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

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