第15章 いろいろなコントロール


今回は、まずリソースエジタでエジットボックス、 スタティクテキスト、ラジオボタン、チェックボタン、プッシュボタンを 持っているダイアログボックスを作ります。

スタティクテキストとは、単なる文字表示です。 左の例でいえば、「表示」という文字です。 エジットボックスは、ユーザーが文字を入力出る 欄のことです。

ラジオボタンとはいくつかグループがあって そのグループ内では1つしか選択のできない ボタンのことです。左の例では、「時刻」「日付」 がそうです。両方を同時に選ぶことはできません。

チェックボックスとはその名の通りいくつでも チェックできるボタンです。

さて、前章ではリソース・エディタを使った場合 resource.hが勝手に作られると書きましたが 今回は、これを無視して自前のヘッダーファイルを作ります。

// timer.h //MENU #define IDM_END 1000 #define IDM_OPTION 2000 //DIALOG BOX #define IDC_STATIC 3000 #define IDC_EDIT1 4000 #define IDC_CHECK1 4010 #define IDC_CHECK2 4020 #define IDC_RADIO1 4030 #define IDC_RADIO2 4040 //TIMER #define ID_MYTIMER 32767

IDC_STATICとか、IDC_EDIT1などはリソース・エディタの 書くコントロールのプロパティで決めた名前です。 その後ろの数値は自分で適当に決めてかまいません。 (intの範囲で)

さて、リソース・スクリプトはどうなったでしょうか。

// timer.rc #include "windows.h" #include "timer.h" ///////////////////////////////////////////////////////////////////////////// // // Dialog // OPTDLG DIALOG DISCARDABLE 0, 0, 117, 77 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "オプション" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,59,50,14 PUSHBUTTON "キャンセル",IDCANCEL,60,59,50,14 LTEXT "表示",IDC_STATIC,8,5,15,8 EDITTEXT IDC_EDIT1,29,6,76,20,ES_AUTOHSCROLL CONTROL "時刻",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON,14,31,30, 10 CONTROL "日付",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,15,46,30, 10 CONTROL "check1",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,67,32,37,10 CONTROL "check2",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,66,44,37,10 END

VC++のリソース・エディタを使っている人は 「あれー、ずいぶん短いな」と思われるかもしれません。 VC++が自動的にいろいろなことをするために 勝手に?いろいろつけ加えているのです。 そのままでももちろんかまいませんが、筆者は よけいなところをバッサリ削除してresource.hも使わないようにしています。 必要なのは、上に示した部分だけです。

では、肝心のソース・プログラムを見てみましょう。 まず、ダイアログボックスで 入力が行われた「モノ」を格納するために グローバル変数を用意します。

エジットボックスから入力された文字列、 ラジオボタンの状態、チェックボックスの状態を格納 するグローバル変数が必要ですね。 それと、前回までダイアログボックスが 画面の隅の方に出てきていたので堂々と 画面の真ん中に出てくるようにする関数を用意しました。 これについては、次回解説します。 では、ソース・プログラムを少しずつ見ていくことにします。

// timer.cpp #include <windows.h> #include <time.h> #include <stdio.h> #include "timer.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE, LPCSTR); BOOL InitInstance(HINSTANCE, LPCSTR, int); int GetTimeStr(void); int SetWinCenter(HWND); HINSTANCE hInst1; char time_str[256]; //取得した時刻をコピーしておくところ char edit_str[256]; //エジットボックスで入力された文字列を格納 int check1 = 0; int check2 = 0; //チェックボタンが押されているかどうか int radio1 = 1; int radio2 = 0; //ラジオボタンが押されているかどうか

この部分は、グローバル変数が増えたのと、 SetWinCenter関数のプロトタイプ宣言が増えただけです。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; char szClassName[] = "timer"; //ウィンドウクラス hInst1 = hCurInst; if (!hPrevInst) { if (!InitApp(hCurInst, szClassName)) return FALSE; } if (!InitInstance(hCurInst, szClassName, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

これは、今までと同じですね。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst, LPCSTR 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 = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL/*"TIMERMENU"*/; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, LPCSTR szClassName, int nCmdShow) { HWND hWnd; HMENU hMenu; int i; hWnd = CreateWindow(szClassName, NULL, //タイトルバーにこの名前が表示されます WS_CAPTION | WS_SYSMENU, //ウィンドウの種類 9999, //X座標 9999, //Y座標 0, //幅 0, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, SW_MINIMIZE); UpdateWindow(hWnd); hMenu = GetSystemMenu(hWnd, FALSE); for (i = 0; i <= 5; i++) DeleteMenu(hMenu, 0, MF_BYPOSITION); AppendMenu(hMenu, MF_STRING, IDM_OPTION, "オプション"); DrawMenuBar(hWnd); return TRUE; }

この部分も同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; char str[256]; char *str_org = "エジットボックス=%s\n" "ラジオボタン1=%d, ボタン2=%d\n" "チェックボックス1=%d, チェック2=%d"; switch (msg) { case WM_SYSCOMMAND: switch (wp) { case IDM_OPTION: id = DialogBox(hInst1, "OPTDLG", hWnd, (DLGPROC)MyDlgProc); if (id == IDOK) { wsprintf((LPTSTR)str, (LPCTSTR)str_org, edit_str, radio1, radio2, check1, check2); MessageBox(hWnd, (LPCSTR)str, (LPCSTR)"表示", MB_OK); } break; default: return(DefWindowProc(hWnd, msg, wp, lp)); break; } break; case WM_CREATE: if(SetTimer(hWnd, ID_MYTIMER, 1000, NULL) == 0) { MessageBox(hWnd, (LPCSTR)"タイマー失敗!", (LPCSTR)"失敗", MB_OK | MB_ICONEXCLAMATION); } break; case WM_TIMER: GetTimeStr(); SetWindowText(hWnd, (LPCSTR)time_str); break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if(KillTimer(hWnd, ID_MYTIMER) == TRUE) { MessageBox(hWnd, (LPCSTR)"タイマーを殺しました!", (LPCSTR)"タイマー削除の成功", MB_OK | MB_ICONEXCLAMATION); } DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

IDM_OPTIONの所を見て下さい。 DialogBox関数を呼び出して、その戻り値がIDOK (OKボタンが押されたら)ならば、 各コントロールの状態をメッセージ・ボックスで 表示しています。ただそれだけです。

int GetTimeStr(void) { char *str_org = "%2d時%2d分%2d秒"; time_t long_time; struct tm *now_time; time(&long_time); now_time = localtime(&long_time); sprintf(time_str, str_org, now_time->tm_hour, now_time->tm_min, now_time->tm_sec); return 0; }

ここも、同じです。 重要なのはダイアログボックスのプロシージャです。

LRESULT CALLBACK MyDlgProc(HWND hDlgWnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_INITDIALOG: SetDlgItemText(hDlgWnd, IDC_EDIT1, (LPCTSTR)edit_str); SendMessage(GetDlgItem(hDlgWnd, IDC_CHECK1), BM_SETCHECK, (WPARAM)check1, 0L); SendMessage(GetDlgItem(hDlgWnd, IDC_CHECK2), BM_SETCHECK, (WPARAM)check2, 0L); SendMessage(GetDlgItem(hDlgWnd, IDC_RADIO1), BM_SETCHECK, (WPARAM)radio1, 0L); SendMessage(GetDlgItem(hDlgWnd, IDC_RADIO2), BM_SETCHECK, (WPARAM)radio2, 0L); SetWinCenter(hDlgWnd); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: GetDlgItemText(hDlgWnd, IDC_EDIT1, (LPTSTR)edit_str, sizeof(edit_str)); if(IsDlgButtonChecked(hDlgWnd, IDC_CHECK1) == BST_CHECKED) { check1 = 1; } else { check1 = 0;} if (IsDlgButtonChecked(hDlgWnd, IDC_CHECK2) == BST_CHECKED) { check2 = 1; } else { check2 = 0;} if(IsDlgButtonChecked(hDlgWnd, IDC_RADIO1) == BST_CHECKED) { radio1 = 1; } else { radio1 = 0;} if(IsDlgButtonChecked(hDlgWnd, IDC_RADIO2) == BST_CHECKED) { radio2 = 1; } else { radio2 = 0;} EndDialog(hDlgWnd, IDOK); break; case IDCANCEL: EndDialog(hDlgWnd, IDCANCEL); break; default: return FALSE; } default: return FALSE; } return TRUE; }

WM_INITDIALOGメッセージの所を見て下さい。 ここでは、ダイアログボックスが作られたときの 初期化みたいなことをしています。 SetDlgItemText関数はエジットボックスに文字列を 表示します。最初に呼ばれたときは何も表示するものがあり ませんが、2回目からは前回の入力を表示するようにします。 ま、文字通りの関数ですから使い方はすぐにわかりますね。

次は、ちょっとやっかいです。 SendMessage関数で各ボタンにメッセージを送りボタンをセットします。 最初の引数はウィンドウハンドルですが、これはダイアログプロシージャの 最初の引数とは異なります。ボタンも1つのウィンドウと考えらます。 従ってダイアログボックスのウィンドウハンドルとは異なる 独自のウィンドウハンドルを持っています。

そのウィンドウハンドルを取得する関数がGetDlgItem関数です。 最初の引数はそのコントロールが属しているダイアログボックスの ウィンドウハンドルで、2番目の引数かコントロールIDです。 戻り値としてそのコントロールのウィンドウハンドルを返します。

SendMessage関数の2番目の引数はボタンをセットするので BM_SETCHECKとします。3番目の引数はセットする値です。 この場合、グローバル変数に用意したものを使います。 4番目の引数は0Lです。

さあ、これでボタンの状態をセットすることができました。 SetWinCenter関数は、自作の関数で中身の説明は 次回します。ウィンドウを画面の真ん中に持っていく関数です。

次に、ダイアログボックスもメニューの時と同じように ボタンが押されたときなどにWM_COMMANDメッセージがでます。 それを捕まえて、OKボタンか、CANCELボタンなら EndDialog関数でダイアログボックスを終了させます。 この関数の第2引数はDialogBox関数の戻り値 になることに 注意して下さい。

OKボタンが押されたとき、グローバル変数に各コントロールの状態を コピーしなくてはいけません。

エジットボックスに入力されている文字列は GetDlgItemText関数で取得することができます。例を見れば引数の 意味は分かりますね。

つぎは、ラジオボタンが押されているかどうか、 チェックボックスがチェックされているかどうかを調べるのが IsDlgButtonChecked関数です。読んで字のごとくですね。 使い方も例題を見ればわかりますね。この関数の戻り値は ボタンが押されていれば1、押されていないときは0です。 BST_CHECKEDは1,BST_UNCHECKEDは0と定義されています。 ここで、グローバル変数に状態をコピーしているのですね。

int SetWinCenter(HWND hWnd) { HWND hDeskWnd; RECT deskrc, rc; int x, y; hDeskWnd = GetDesktopWindow(); GetWindowRect(hDeskWnd, (LPRECT)&deskrc); GetWindowRect(hWnd, (LPRECT)&rc); x = (deskrc.right - (rc.right-rc.left)) / 2; y = (deskrc.bottom - (rc.bottom-rc.top)) / 2; SetWindowPos(hWnd, HWND_TOP, x, y, (rc.right-rc.left), (rc.bottom-rc.top),SWP_SHOWWINDOW); return 0; }

さて、最後はサービスの関数です。ウィンドウを画面の中央に 持っていく関数です。似たようなものはいろいろな 入門書に出ていますね。解説は次回します。
[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

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