また、メニューの「表示」でツールバーやステータスバーを
非表示にできるようにもします。メニュー項目にチェックをつけたり
消したりもします。今回は盛りだくさんです。でもたいして難しくはありません。
左の図のように、ツールバーのボタンのツールチップと同じ文章が
ステータスバーにも表示されています。
また、アプリケーションのアイコンも作ってみました。
メニュー項目にチェックを付けます。
プロパティシートの「更新」ボタンはユーザーが
入力項目を少しでも変更すると使えるようになります。
「更新」ボタンを押してデータが更新されると
再び「更新」ボタンは使用不能となります。
左の図のようにプロパティシートにBMPを表示するのは 簡単です。
それと、親ウィンドウに表示されていたアイコンと プロパティシートに付いているアイコンが 異なることにも注意してください。
まずは、プロパティシートの「更新」ボタンの使い方です。
前章のように PROPSHEETHEADER
構造体のdwFlagsメンバを PSH_NOAPPLYNOWにしなければ
自動的に使用不可の「更新」ボタンが出現します。
各ページに変更があった場合PropSheet_Changedマクロを
実行してプロパティシートに情報更新を知らせます。
すると、「更新」ボタンが使用可能になります。
そして、更新が起こると再度「更新」ボタンは使用不能に
なります。
hPropSheetDlgは、プロパティシートのハンドルBOOL PropSheet_Changed( hPropSheetDlg, hwndPage );
まずはプログラムを見てみましょう。
まずは、ストリングテーブルを注目してみてください。 今回は、ツールバーボタンに表示するテキストのIDとコマンドのID を同じにしてみました。ツールチップに表示するテキストは コマンドIDとは別にしてみました。このへんはプログラムの書き方で どうにでもなります。// 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_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; }
これは、あんまり説明はいらないですね。 uIDCheckにメニューのIDを設定した場合uCheckにはMF_COMMANDが メニュー位置を設定した場合はuCheckにはMF_BYPOSITIONが来ます。 uCheckはMF_CHECKEDならチェックが付いて、 MF_UNCHECKEDならチェックが付きません。これとMF_BYCOMMANDまたは MF_BYPOSITIONの組み合わせとなります。DWORD CheckMenuItem( HMENU hmenu, // handle to menu UINT uIDCheckItem, // menu item to check or uncheck UINT uCheck // menu item flags );
それと、ツールチップが出ているときにステータスバーに ツールチップと同じテキストを表示する部分を見てください。 これは、わかると思いますが問題は表示させた後、 ボタンをクリックするなりマウスカーソルを親ウィンドウの クライアント領域に移動した場合最後に表示した テキストがステータスバーに残っていまいます。 これを防ぐ方法はいろいろありますが、ここでは TTN_POPメッセージを処理してみました。
これは、ツールチップが非表示になる予定の時出されます。 これを捕まえてステータスバーに表示を出せばよいですね。 あとは、目新しいものはありません。TTN_POP idTT = (int) wParam; pnmh = (NMHDR FAR *) lParam;
プロパティシートの1ページ目のプロシージャです。 各エジットコントロールが変更されたらPropSheet_Changedマクロを呼んでいます。 また、OKボタンまたは更新ボタンが押されたら変数str[]に各アイテムからテキストを 取得してコピーしています。このときIDC_EDIT1, IDC_EDIT2,...が 続き番号に なっていないとこのような書き方はできません。 ヘッダーファイルを確認してみてください。// 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; }
2ページ目のプロパティシートのプロシージャです。 チェックボックスをクリックするとするときにWM_COMMANDメッセージが 出されるのでこれを調べれば更新されたかどうかわかります。 ここでも、IDC_CHECK1, IDC_CHECK2,...が続き番号でないと 書けないプログラムになっています。 こういうテクニックはよく使われます。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; }
プロパティシートを作る関数です。ここでpsh.dwFlagsにPSH_USEICONIDを 設定しているのでpsh.pszIconが有効になり"MYICON"がプロパティシートの アイコンとなります。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; }
ツールバーを作る関数です。これも、前回までの知識で十分理解できると思います。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