ところで、このプロパティシートはどのように作ればよいのでしょうか。
VC++のリソース関係のメニューを見ても「プロパティシートの新規作成」
なんていうのはありません。
実は、リソースはダイアログボックスのものを使います。
では、最初に今回作るサンプルを見てみましょう。
プログラムを実行すると右のようなウィンドウが現れます。
このメニューの中の「オプション」「設定」を選ぶと、
左のような、プロパティシートが現れます。
各項目を記入して「OK」ボタンを押すとそれが、親ウィンドウの
クライアント領域に表示されます。また、このプロパティシートでは
「更新」ボタンがないことに注意してください。
では、さっそく作り方の説明をします。
まず、各ページをダイアログボックスのリソースを 作るときの要領で作ります。
プロパティシートが3ページあればダイアログボックスのリソースが 3つできます。それぞれのリソース名を"PAGE1", "PAGE2", "PAGE3" などとします(名前は何でもよい)。
次に、各ページについてPROPSHEETPAGE構造体を設定して、 CreatePropertySheetPage関数を実行し各ページを作ります。 各ページができたら、PROPSHEETHEADER 構造体を設定して、 PropertySheet関数を実行すれば終わりです。 また、各ページはダイアログボックスですから、それぞれ ダイアログプロシージャがあってそれぞれ処理を行います。 では、まずPROPSHEETPAGE構造体から見てみましょう。
dwSizeは、この構造体のサイズです。typedef struct _PROPSHEETPAGE { // psp DWORD dwSize; DWORD dwFlags; HINSTANCE hInstance; union { LPCTSTR pszTemplate; LPCDLGTEMPLATE pResource; }; union { HICON hIcon; LPCTSTR pszIcon; }; LPCTSTR pszTitle; DLGPROC pfnDlgProc; LPARAM lParam; LPFNPSPCALLBACK pfnCallback; UINT FAR * pcRefParent; } PROPSHEETPAGE, FAR *LPPROPSHEETPAGE; typedef const PROPSHEETPAGE FAR *LPCPROPSHEETPAGE;
dwFlagsは、この構造体のどのメンバを利用するかを指定します。
PSP_DEFAULTは、デフォルトです。
PSP_HASHELPは、このページがアクティブになったら「ヘルプ」ボタンを
表示します。
PSP_USEHICONは、タブの小さなアイコンとして共用体のhIconを使用します。
PSP_USEICONIDは、アイコンリソースの名前または、タブの小さなアイコンとして
共用体のpszIconを使用します。
hInstanceは、インスタンスハンドル。
pszTemplateは、ダイアログボックスのリソース名を指定します。
pfnDlgProcは、ダイアログボックスプロシージャへのポインタを指定します。
その他についてはヘルプ等を参照してください。
ページの設定が終わったら、ページをCreatePropertySheetPage関数で作ります。
lppspは、先ほど設定したPROPSHEETPAGE構造体へのポインタです。 戻り値は、成功すれば新しいページのハンドル、失敗したときはNULLを返します。 各ページを作ったら、PROPSHEETHEADER構造体を設定します。HPROPSHEETPAGE CreatePropertySheetPage( LPCPROPSHEETPAGE lppsp );
dwSizeは、この構造体のサイズです。typedef struct _PROPSHEETHEADER { // psh DWORD dwSize; DWORD dwFlags; HWND hwndParent; HINSTANCE hInstance; union { HICON hIcon; LPCTSTR pszIcon; }; LPCTSTR pszCaption; UINT nPages union { UINT nStartPage LPCTSTR pStartPage; }; union { LPCPROPSHEETPAGE ppsp; HPROPSHEETPAGE FAR *phpage; }; PFNPROPSHEETCALLBACK pfnCallback; } PROPSHEETHEADER, FAR *LPPROPSHEETHEADER; typedef const PROPSHEETHEADER FAR *LPCPROPSHEETHEADER;
最後に、PropertySheet関数を呼び出します。
lppsphは、先ほど設定したPROPSHEETHEADER構造体へのポインタです。int PropertySheet( LPCPROPSHEETHEADER lppsph );
ところで、プロパティシートで「OK」ボタンが押されたときの
処理は、どこで行えばよいのか?ということがありますが、
各ダイアログプロシージャで行うのが一番簡単です。
WM_NOTIFYメッセージを捕まえて、NMHDR構造体のcodeメンバが
PSN_APPLYであれば、「OK」ボタンが押されたことになります。
どういう場合に、どこのプロシージャにWM_NOTIFYメッセージが届くかは
自分で各プロシージャのいろいろなところにMessageBox関数を
書いて調べるとよくわかります。
今回は、更新ボタンとモードレスにする方法については触れませんでした。
また、後の章で解説する予定です。
次に、resource.hの一部を示します。自分でヘッダーファイルを作る人は これを参考にしてください。// tab01.rc // 自前で作る人はwindows.hと、自作のヘッダーファイルをインクルード ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "オプション(&O)" BEGIN MENUITEM "設定(&S)", IDM_OPTION END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // PAGE1 DIALOG DISCARDABLE 0, 0, 126, 47 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "氏名・年齢" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,59,7,60,13,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,59,23,60,13,ES_AUTOHSCROLL LTEXT "氏名:",IDC_STATIC,7,7,18,8 LTEXT "年齢:",IDC_STATIC,7,28,18,8 END PAGE2 DIALOG DISCARDABLE 0, 0, 126, 47 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "住所・電話" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT4,31,28,88,12,ES_AUTOHSCROLL EDITTEXT IDC_EDIT3,31,7,88,12,ES_AUTOHSCROLL LTEXT "住所:",IDC_STATIC,7,7,21,11 LTEXT "電話:",IDC_STATIC,7,29,21,11 END PAGE3 DIALOG DISCARDABLE 0, 0, 126, 47 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "性別" FONT 9, "MS Pゴシック" BEGIN CONTROL "男性",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON,39,7,30,10 CONTROL "女性",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,39,17,30, 10 CONTROL "不明",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON,39,27,30, 10 END
次に、ソースファイルを示します。// resource.hの一部 #define IDC_EDIT1 1000 #define IDC_EDIT2 1001 #define IDC_EDIT3 1003 #define IDC_EDIT4 1004 #define IDC_RADIO1 1005 #define IDC_RADIO2 1006 #define IDC_RADIO3 1007 #define IDM_OPTION 40001 #define IDM_END 40002
seibetsuの初期状態は「不明」(3)にしておきました。// tab01.cpp #define STRICT #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlg1Proc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlg2Proc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlg3Proc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int seibetsu = 3; HWND hParent; //親ウィンドウ void CreateMyProp(HWND); char szClassName[] = "tab0101"; //ウィンドウクラス char str_name[64], str_age[64], str_address[64], str_phone[64]; char *str1_org = "名前 = %s"; char *str2_org = "年齢 = %s"; char *str3_org = "住所 = %s"; char *str4_org = "電話 = %s"; char *str5_org = "性別 = %s";
これは、いつもと同じです。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 = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); }
今回は、ディスプレイの幅、高さをGetSystemMetrics関数で 求めておいて、親ウィンドウをディスプレイの中央に表示するようにしてみました。 第16章の方法と比べてみてください。//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; int wmax, hmax; wmax = GetSystemMetrics(SM_CXSCREEN); hmax = GetSystemMetrics(SM_CYSCREEN); hWnd = CreateWindow(szClassName, "猫でもわかるプロパティシート", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 (wmax - 300) / 2, //X座標 (hmax - 150) / 2, //Y座標 300, //幅 150, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; // 親のウィンドウハンドルをグローバル変数に保存 hParent = hWnd; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
nIndexにいろいろなものを設定すると、いろいろなものが取得できます。 システム メトリックとは、Windowsが表示するさまざまな表示要素の幅と高さのことです。int GetSystemMetrics( int nIndex );
プロパティシートで設定した事項をWM_PAINTのところで、クライアント領域に 表示させています。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HDC hdc; PAINTSTRUCT ps; char str[64], sex[6]; switch (msg) { case WM_CREATE: InitCommonControls(); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_OPTION: CreateMyProp(hWnd); break; case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0L); break; } break; case WM_PAINT: switch (seibetsu) { case 1: strcpy(sex, "男性"); break; case 2: strcpy(sex, "女性"); break; case 3: strcpy(sex, "不明"); break; } hdc = BeginPaint(hWnd, &ps); wsprintf(str, str1_org, str_name); TextOut(hdc, 0, 0, str, strlen(str)); wsprintf(str, str2_org, str_age); TextOut(hdc, 0, 20, str, strlen(str)); wsprintf(str, str3_org, str_address); TextOut(hdc, 0, 40, str, strlen(str)); wsprintf(str, str4_org, str_phone); TextOut(hdc, 0, 60, str, strlen(str)); wsprintf(str, str5_org, sex); TextOut(hdc, 0, 80, str, strlen(str)); EndPaint(hWnd, &ps); 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; }
各ページのダイアログプロシージャです。 PSN_APPLYメッセージの処理の仕方に注意してください。LRESULT CALLBACK MyDlg1Proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { NMHDR *nmhdr; switch(msg) { case WM_INITDIALOG: Edit_SetText(GetDlgItem(hWnd, IDC_EDIT1), str_name); Edit_SetText(GetDlgItem(hWnd, IDC_EDIT2), str_age); return TRUE; case WM_NOTIFY: nmhdr = (NMHDR *)lp; switch(nmhdr->code) { case PSN_APPLY: Edit_GetText(GetDlgItem(hWnd, IDC_EDIT1), str_name, sizeof(str_name)); Edit_GetText(GetDlgItem(hWnd, IDC_EDIT2), str_age, sizeof(str_age)); InvalidateRect(hParent, NULL, TRUE); break; default: return FALSE; } break; } return FALSE; } LRESULT CALLBACK MyDlg2Proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { NMHDR *nmhdr; switch(msg) { case WM_INITDIALOG: Edit_SetText(GetDlgItem(hWnd, IDC_EDIT3), str_address); Edit_SetText(GetDlgItem(hWnd, IDC_EDIT4), str_phone); return TRUE; case WM_NOTIFY: nmhdr = (NMHDR *)lp; switch(nmhdr->code) { case PSN_APPLY: Edit_GetText(GetDlgItem(hWnd, IDC_EDIT3), str_address, sizeof(str_address)); Edit_GetText(GetDlgItem(hWnd, IDC_EDIT4), str_phone, sizeof(str_phone)); InvalidateRect(hParent, NULL, TRUE); return TRUE; default: return FALSE; } break; } return FALSE; } LRESULT CALLBACK MyDlg3Proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { NMHDR *nmhdr; switch(msg) { case WM_INITDIALOG: switch (seibetsu) { case 1: Button_SetCheck(GetDlgItem(hWnd, IDC_RADIO1), BST_CHECKED); break; case 2: Button_SetCheck(GetDlgItem(hWnd, IDC_RADIO2), BST_CHECKED); break; case 3: Button_SetCheck(GetDlgItem(hWnd, IDC_RADIO3), BST_CHECKED); break; } return TRUE; case WM_NOTIFY: nmhdr = (NMHDR *)lp; switch (nmhdr->code) { case PSN_APPLY: if(Button_GetCheck(GetDlgItem(hWnd, IDC_RADIO1)) == BST_CHECKED) seibetsu = 1; if(Button_GetCheck(GetDlgItem(hWnd, IDC_RADIO2)) == BST_CHECKED) seibetsu = 2; if(Button_GetCheck(GetDlgItem(hWnd, IDC_RADIO3)) == BST_CHECKED) seibetsu = 3; InvalidateRect(hParent, NULL, TRUE); return TRUE; default: return FALSE; } break; } return FALSE; }
前述のプロパティシートの作り方の解説を参考にソースコードを 読んでみてください。void CreateMyProp(HWND hWnd) { HINSTANCE hInst; PROPSHEETPAGE psp; PROPSHEETHEADER psh; HPROPSHEETPAGE hpsp[3]; 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); psp.pszTemplate = "PAGE3"; psp.pfnDlgProc = (DLGPROC)MyDlg3Proc; hpsp[2] = CreatePropertySheetPage(&psp); memset(&psh, 0, sizeof(PROPSHEETHEADER)); psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_NOAPPLYNOW; psh.hInstance = hInst; psh.hwndParent = hWnd; psh.nPages = 3; psh.phpage = hpsp; psh.pszCaption = "設定"; (HWND)PropertySheet(&psh); return; }
[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]
Update Aug/09/1997 By Y.Kumei