第68章 いろいろ付けてみる


今回は、今までやったコモンコントロールをいろいろ付けてみます。 また、前回のプロパティシートでは「更新」ボタンは削除してありました。 今回は、より一般的に「更新」ボタンも付けてみます。 また、ツールバーのボタンに対するツールチップと同じ内容の 文章をステータスバーに表示する方法についても考えてみます。 さらに、ステータスバーの右の方のパーツには時計も表示します。

また、メニューの「表示」でツールバーやステータスバーを 非表示にできるようにもします。メニュー項目にチェックをつけたり 消したりもします。今回は盛りだくさんです。でもたいして難しくはありません。

左の図のように、ツールバーのボタンのツールチップと同じ文章が ステータスバーにも表示されています。 また、アプリケーションのアイコンも作ってみました。

メニュー項目にチェックを付けます。

プロパティシートの「更新」ボタンはユーザーが 入力項目を少しでも変更すると使えるようになります。 「更新」ボタンを押してデータが更新されると 再び「更新」ボタンは使用不能となります。

左の図のようにプロパティシートにBMPを表示するのは 簡単です。

それと、親ウィンドウに表示されていたアイコンと プロパティシートに付いているアイコンが 異なることにも注意してください。



まずは、プロパティシートの「更新」ボタンの使い方です。 前章のように PROPSHEETHEADER 構造体のdwFlagsメンバを PSH_NOAPPLYNOWにしなければ 自動的に使用不可の「更新」ボタンが出現します。 各ページに変更があった場合PropSheet_Changedマクロを 実行してプロパティシートに情報更新を知らせます。 すると、「更新」ボタンが使用可能になります。 そして、更新が起こると再度「更新」ボタンは使用不能に なります。

BOOL PropSheet_Changed( hPropSheetDlg, hwndPage );

hPropSheetDlgは、プロパティシートのハンドル
hwndPageは、更新のあったページのハンドルを表します。 さて、各ページに変更があったかどうかを調べるにはどうしたらよいのでしょうか。 実は、WM_COMMANDメッセージを捕まえて各コントロールのIDを チェックします。エジットボックスであれば変更があれば HIWORD(wParam)がEN_CHANGEとなります。チェックボックスであれば そのコントロールのIDをチェックするだけでOKです。

まずはプログラムを見てみましょう。

// tab02.rc #include <windows.h> #include "tab02.h" ///////////////////////////////////////////////////////////////////////////// // // Dialog // PAGE1 DIALOG DISCARDABLE 0, 0, 187, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "個人情報" FONT 9, "MS Pゴシック" BEGIN LTEXT "氏名:",IDC_STATIC,7,7,18,8 LTEXT "年齢:",IDC_STATIC,7,30,18,8 LTEXT "住所:",IDC_STATIC,7,53,18,8 LTEXT "電話:",IDC_STATIC,7,76,18,8 EDITTEXT IDC_EDIT1,56,7,124,11,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,56,29,124,11,ES_AUTOHSCROLL EDITTEXT IDC_EDIT3,56,51,124,11,ES_AUTOHSCROLL EDITTEXT IDC_EDIT4,56,73,124,11,ES_AUTOHSCROLL END PAGE2 DIALOG DISCARDABLE 0, 0, 187, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ペット" FONT 9, "MS Pゴシック" BEGIN CONTROL "猫を飼っている",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,59,10 CONTROL "犬を飼っている",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,30,59,10 CONTROL "金魚を飼っている",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,53,66,10 CONTROL "人間を飼っている?",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,76,73,10 CONTROL "MYBMP",IDC_STATIC,"Static",SS_BITMAP | SS_SUNKEN,86,7, 94,79 END ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "表示(&V)" BEGIN MENUITEM "ツールバー(&T)", IDM_TOOL MENUITEM "ステータスバー(&S)", IDM_STATUS END POPUP "オプション(&O)" BEGIN MENUITEM "設定(&S)", IDM_SET END POPUP "ヘルプ(&H)" BEGIN MENUITEM "..について(&A)", IDM_ABOUT END END ///////////////////////////////////////////////////////////////////////////// // // Icon // MYICON ICON DISCARDABLE "icon1.ico" MYICON2 ICON DISCARDABLE "icon2.ico" ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP BITMAP DISCARDABLE "mybmp.bmp" ID_MYBMP BITMAP DISCARDABLE "id_mtbmp.bmp" ///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE DISCARDABLE BEGIN IDM_END "終了" IDM_SET "設定" IDM_ABOUT "ヘルプ" IDS_TIPS_END "アプリケーションを終了します" IDS_TIPS_SET "各種設定を行います" IDS_TIPS_ABOUT "このアプリケーションについて" END

まずは、ストリングテーブルを注目してみてください。 今回は、ツールバーボタンに表示するテキストのIDとコマンドのID を同じにしてみました。ツールチップに表示するテキストは コマンドIDとは別にしてみました。このへんはプログラムの書き方で どうにでもなります。

次にビットマップを見てください。ID_MYBMP(ヘッダーファイルで 値が定義されている)と"MYBMP"を使い分けています。 ID_MYBMPはCreateToolbarExの関係でこのような形にしています。 ビットマップや、アイコンは適当に作って用意しておいてください。

// tab02.h #define ID_MYTIMER 32767 #define ID_MYTOOLBAR 200 #define ID_MYSTATUSBAR 201 #define ID_MYBMP 111 #define IDC_STATIC 999 #define IDC_EDIT1 1000 #define IDC_EDIT2 1001 #define IDC_EDIT3 1002 #define IDC_EDIT4 1003 #define IDC_CHECK1 1101 #define IDC_CHECK2 1102 #define IDC_CHECK3 1103 #define IDC_CHECK4 1104 #define IDM_END 40001 #define IDM_SET 40002 #define IDM_ABOUT 40004 #define IDM_TOOL 40005 #define IDM_STATUS 40006 #define IDS_TIPS_END 40007 #define IDS_TIPS_SET 40008 #define IDS_TIPS_ABOUT 40009

ヘッダーファイルはどうということもないですね。

// tab02.cpp #define STRICT #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "tab02.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlg1Proc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlg2Proc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); HWND MakeMyToolbar(HWND); void MakeMyProp(HWND); HWND MakeMyStatus(HWND); char szClassName[] = "tab02"; //ウィンドウクラス HWND hPropSheet; int nButton[4]; char str[4][256]; HWND hParent; TBBUTTON tbb[3] = { {0, IDM_SET, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {1, IDM_END, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, {2, IDM_ABOUT, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0}, }; 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(hInst, "MYICON2"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 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; hParent = hWnd; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

このへんもあまり変わりはありませんが、親ウィンドウのハンドルを グローバル変数にコピーして保存しておきます。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; int h_tool; int nParts[2]; RECT rcTOOL; HDC hdc; static HWND hToolWnd; static HWND hStatusWnd; PAINTSTRUCT ps; static HMENU hMenu; static int tool; static int status; LPTOOLTIPTEXT lpttt; static HINSTANCE hInst; char buf[256]; char *str_org1 = "氏名 = %s"; char *str_org2 = "年齢 = %s 歳"; char *str_org3 = "住所 = %s"; char *str_org4 = "電話 = %s"; char *str_chk = "猫= %s 犬= %s 金魚= %s 人間= %s"; char pet[4][8]; hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); CheckMenuItem(hMenu, IDM_TOOL, MF_BYCOMMAND | MF_CHECKED); tool = 1; CheckMenuItem(hMenu, IDM_STATUS, MF_BYCOMMAND | MF_CHECKED); status = 1; SetTimer(hWnd, ID_MYTIMER, 500, NULL); InitCommonControls(); hToolWnd = MakeMyToolbar(hWnd); hStatusWnd = MakeMyStatus(hWnd); break; case WM_NOTIFY: switch (((LPNMHDR)lp)->code) { case TTN_NEEDTEXT: lpttt = (LPTOOLTIPTEXT)lp; lpttt->hinst = hInst; id = lpttt->hdr.idFrom; switch (id) { case IDM_END: lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_END); LoadString(hInst, IDS_TIPS_END, buf, sizeof(buf) - 1); SendMessage(hStatusWnd, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)buf); break; case IDM_SET: lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_SET); LoadString(hInst, IDS_TIPS_SET, buf, sizeof(buf) - 1); SendMessage(hStatusWnd, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)buf); break; case IDM_ABOUT: lpttt->lpszText = MAKEINTRESOURCE(IDS_TIPS_ABOUT); LoadString(hInst, IDS_TIPS_ABOUT, buf, sizeof(buf) - 1); SendMessage(hStatusWnd, SB_SETTEXT, (WPARAM)0 | 0, (LPARAM)buf); break; } break; case TTN_POP: SendMessage(hStatusWnd, SB_SETTEXT, 0, (LPARAM)"猫でもわかる"); break; } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0L); break; case IDM_SET: MakeMyProp(hWnd); break; case IDM_ABOUT: MessageBox(hWnd, "猫でもわかるプロパティシート", "About", MB_OK); break; case IDM_TOOL: switch (tool) { case 1: ShowWindow(hToolWnd, SW_HIDE); CheckMenuItem(hMenu, IDM_TOOL, MF_BYCOMMAND | MF_UNCHECKED); tool = 0; break; case 0: ShowWindow(hToolWnd, SW_SHOW); CheckMenuItem(hMenu, IDM_TOOL, MF_BYCOMMAND | MF_CHECKED); tool = 1; break; } break; case IDM_STATUS: switch (status) { case 1: ShowWindow(hStatusWnd, SW_HIDE); CheckMenuItem(hMenu, IDM_STATUS, MF_BYCOMMAND | MF_UNCHECKED); status = 0; break; case 0: ShowWindow(hStatusWnd, SW_SHOW); CheckMenuItem(hMenu, IDM_STATUS, MF_BYCOMMAND | MF_CHECKED); status = 1; break; } break; } break; case WM_SIZE: nParts[0] = LOWORD(lp) - 100; nParts[1] = LOWORD(lp); SendMessage(hToolWnd, WM_SIZE, wp, lp); SendMessage(hStatusWnd, WM_SIZE, wp, lp); SendMessage(hStatusWnd, SB_SETPARTS, 2, (LPARAM)nParts); break; case WM_PAINT: GetWindowRect(hToolWnd, &rcTOOL); h_tool = rcTOOL.bottom - rcTOOL.top; for (id = 0; id <= 3; id++) { if (nButton[id] == BST_CHECKED) strcpy(pet[id], "○"); if (nButton[id] != BST_CHECKED) strcpy(pet[id], "×"); } hdc = BeginPaint(hWnd, &ps); wsprintf(buf, str_org1, str[0]); TextOut(hdc, 0, 0 + h_tool, buf, strlen(buf)); wsprintf(buf, str_org2, str[1]); TextOut(hdc, 0, 20 + h_tool, buf, strlen(buf)); wsprintf(buf, str_org3, str[2]); TextOut(hdc, 0, 40 + h_tool, buf, strlen(buf)); wsprintf(buf, str_org4, str[3]); TextOut(hdc, 0, 60 + h_tool, buf, strlen(buf)); wsprintf(buf, str_chk, pet[0], pet[1], pet[2], pet[3]); TextOut(hdc, 0, 100 + h_tool, buf, strlen(buf)); EndPaint(hWnd, &ps); break; case WM_TIMER: { SYSTEMTIME st; char str_tm[256]; char *str_tm_org = "%2d時%2d分%2d秒"; GetLocalTime(&st); wsprintf(str_tm, str_tm_org, st.wHour, st.wMinute, st.wSecond); SendMessage(hStatusWnd, SB_SETTEXT, (WPARAM)1 | 0, (LPARAM)str_tm); } break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: KillTimer(hWnd, ID_MYTIMER); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

うーん、こりゃ長いね

でも、1つ1つ見ていくと大して難しくありません。 今回初めて出てきたものに

DWORD CheckMenuItem( HMENU hmenu, // handle to menu UINT uIDCheckItem, // menu item to check or uncheck UINT uCheck // menu item flags );

これは、あんまり説明はいらないですね。 uIDCheckにメニューのIDを設定した場合uCheckにはMF_COMMANDが メニュー位置を設定した場合はuCheckにはMF_BYPOSITIONが来ます。 uCheckはMF_CHECKEDならチェックが付いて、 MF_UNCHECKEDならチェックが付きません。これとMF_BYCOMMANDまたは MF_BYPOSITIONの組み合わせとなります。

それと、ツールチップが出ているときにステータスバーに ツールチップと同じテキストを表示する部分を見てください。 これは、わかると思いますが問題は表示させた後、 ボタンをクリックするなりマウスカーソルを親ウィンドウの クライアント領域に移動した場合最後に表示した テキストがステータスバーに残っていまいます。 これを防ぐ方法はいろいろありますが、ここでは TTN_POPメッセージを処理してみました。

TTN_POP idTT = (int) wParam; pnmh = (NMHDR FAR *) lParam;

これは、ツールチップが非表示になる予定の時出されます。 これを捕まえてステータスバーに表示を出せばよいですね。 あとは、目新しいものはありません。

// 1ページ目のダイアログプロシージャ LRESULT CALLBACK MyDlg1Proc(HWND hDlgWnd, UINT msg, WPARAM wp, LPARAM lp) { NMHDR *nmhdr; int i; switch(msg) { case WM_INITDIALOG: for (i = 0; i <= 3; i++) Edit_SetText(GetDlgItem(hDlgWnd, IDC_EDIT1 + i), str[i]); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDC_EDIT1: case IDC_EDIT2: case IDC_EDIT3: case IDC_EDIT4: if (HIWORD(wp) == EN_CHANGE) { PropSheet_Changed(GetParent(hDlgWnd), hDlgWnd); return TRUE; } } return FALSE; case WM_NOTIFY: nmhdr = (LPNMHDR)lp; switch (nmhdr->code) { case PSN_APPLY: for (i = 0; i <= 3; i++) { Edit_GetText(GetDlgItem(hDlgWnd, IDC_EDIT1 + i), str[i], sizeof(str[i])); } InvalidateRect(hParent, NULL, TRUE); return TRUE; } return FALSE; } return FALSE; }

プロパティシートの1ページ目のプロシージャです。 各エジットコントロールが変更されたらPropSheet_Changedマクロを呼んでいます。 また、OKボタンまたは更新ボタンが押されたら変数str[]に各アイテムからテキストを 取得してコピーしています。このときIDC_EDIT1, IDC_EDIT2,...が 続き番号に なっていないとこのような書き方はできません。 ヘッダーファイルを確認してみてください。

LRESULT CALLBACK MyDlg2Proc(HWND hDlgWnd, UINT msg, WPARAM wp, LPARAM lp) { LPNMHDR nmhdr; int i; switch (msg) { case WM_INITDIALOG: for (i = 0; i <= 3; i++) { if (nButton[i] == BST_CHECKED) Button_SetCheck(GetDlgItem(hDlgWnd, IDC_CHECK1 + i), BST_CHECKED); } return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDC_CHECK1: case IDC_CHECK2: case IDC_CHECK3: case IDC_CHECK4: PropSheet_Changed(GetParent(hDlgWnd), hDlgWnd); return TRUE; } case WM_NOTIFY: nmhdr = (LPNMHDR)lp; switch (nmhdr->code) { case PSN_APPLY: for (i = 0; i <= 3; i++) { nButton[i] = Button_GetCheck(GetDlgItem(hDlgWnd, IDC_CHECK1 + i)); } return TRUE; } return FALSE; } return FALSE; }

2ページ目のプロパティシートのプロシージャです。 チェックボックスをクリックするとするときにWM_COMMANDメッセージが 出されるのでこれを調べれば更新されたかどうかわかります。 ここでも、IDC_CHECK1, IDC_CHECK2,...が続き番号でないと 書けないプログラムになっています。 こういうテクニックはよく使われます。

void MakeMyProp(HWND hWnd) { HINSTANCE hInst; PROPSHEETPAGE psp; PROPSHEETHEADER psh; HPROPSHEETPAGE hpsp[2]; hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); psp.dwSize = sizeof(PROPSHEETPAGE); psp.dwFlags = PSP_DEFAULT; psp.hInstance = hInst; psp.pszTemplate = "PAGE1"; psp.pfnDlgProc = (DLGPROC)MyDlg1Proc; hpsp[0] = CreatePropertySheetPage(&psp); psp.pszTemplate = "PAGE2"; psp.pfnDlgProc = (DLGPROC)MyDlg2Proc; hpsp[1] = CreatePropertySheetPage(&psp); memset(&psh, 0, sizeof(PROPSHEETHEADER)); psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_USEICONID; psh.hInstance = hInst; psh.hwndParent = hWnd; psh.pszIcon = "MYICON"; psh.nPages = 2; psh.phpage = hpsp; psh.pszCaption = "設定"; hPropSheet = (HWND)PropertySheet(&psh); return; }

プロパティシートを作る関数です。ここでpsh.dwFlagsにPSH_USEICONIDを 設定しているのでpsh.pszIconが有効になり"MYICON"がプロパティシートの アイコンとなります。

HWND MakeMyToolbar(HWND hWnd) { HWND hTool; HINSTANCE hInst; int i; char szText[64]; hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); hTool = CreateToolbarEx( hWnd, WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS, ID_MYTOOLBAR, 3, hInst, ID_MYBMP, tbb, 0, 0, 0, 32, 32, sizeof(TBBUTTON)); for (i = 0; i <= 2; i++) { LoadString(hInst, tbb[i].idCommand, szText, sizeof(szText) - 1); tbb[i].iString = SendMessage(hTool, TB_ADDSTRING, 0, (LPARAM)szText); } SendMessage(hTool, TB_ADDBUTTONS, 3, (LPARAM)&tbb[0]); SendMessage(hTool, TB_AUTOSIZE, 0, 0); return hTool; }

ツールバーを作る関数です。これも、前回までの知識で十分理解できると思います。

HWND MakeMyStatus(HWND hWnd) { HWND hStatus; hStatus = CreateStatusWindow( WS_CHILD | WS_VISIBLE, "粂井康孝・制作", hWnd, ID_MYSTATUSBAR); return hStatus; }

これは、ステータスバーを表示する関数で別にどうということもありません。

あー、疲れた!!




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

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