第213章 ウィザード97を作ろう


今回は「ウィザード97」というウィザードを作ります。 ヘッダーとか、ウォーターマーク(透かし)などのビットマップを 表示することができます。しかし、この仕様は過渡的なもので 将来変更される可能性もあります。

作り方は簡単です。 PROPSHEETHEADER構造体のdwFlagsにPSH_WIZARD97 を指定すればよいのですが VC++6.0のヘルプには、PSH_WIZARD97とこれに関するすべての フラッグは現在インプリメントされていない、と書かれています。 筆者が試したところヘッダに関してはどうもだめですが、ウォーターマークは きちんと表示されるようです。



また、今回は「次へ」ボタンを押した時に、エディットコントロールに 何も書きこまれていない時は、注意のダイアログボックスを出して 次のページに進まないようにします。

また、一度設定した項目について後で一部修正することも考慮します。 一度設定した項目の一部を修正する時、ウィザードを実行した場合 すべての項目を最初から入力しなおすのはめんどうなものです。 そこで、一度設定した項目は次回ウィザードが出てきた時は、 その設定がエディットボックスに表示されるようにします。 そのページの設定に修正がない場合はそのまま「次へ」または「完了」 ボタンを押します。

ウィザード97では、左の図のようにウォーターマーク、 ヘッダータイトル、ヘッダーサブタイトルなどが使えます。



では、新しいPROPSHEETHEADER構造体をみてみましょう。

typedef struct _PROPSHEETHEADER { DWORD dwSize; DWORD dwFlags; HWND hwndParent; HINSTANCE hInstance; union { HICON hIcon; LPCTSTR pszIcon; }; LPCSTR pszCaption; UINT nPages; union { UINT nStartPage; LPCTSTR pStartPage; }; union { LPCPROPSHEETPAGE ppsp; HPROPSHEETPAGE FAR *phpage; }; PFNPROPSHEETCALLBACK pfnCallback; #if (_WIN32_IE >= 0x0400) union { HBITMAP hbmWatermark; LPCTSTR pszbmWatermark; }; HPALETTE hplWatermark; union { HBITMAP hbmHeader; LPCSTR pszbmHeader; }; #endif } PROPSHEETHEADER, FAR *LPPROPSHEETHEADER;

以前の定義とどこが違うかというと#ifと#endifではさまれた 部分が追加されていることです。

hbmWatermarkは、透かしのビットマップのハンドルを指定します。 dwFlagsにPSH_USEHBMWATERMARKが指定されていないと無視されます。

pszbmWatermarkは透かしのためのビットマップリソースの名前を指定します。 PSH_USEHBMWATERMARKが指定されていると無視されます。

hplWatermarkには、ヘッダーやウォーターマークのためのビットマップの パレットハンドルを指定します。dwFlagsにPSH_USEHPLWATERMARK が指定されていないと無視されます。

hbmHeaderには、ヘッダになるビットマップのハンドルを指定します。 dwFlagsにPSH_USEHBMHEADERが指定されていないと無視されます。

pszbmHeaderには、ヘッダになるビットマップリソースの名前を指定します。 dwFlagsにPSH_USEHBMHEADERが指定されていると無視されます。

同様にPROPSHEETPAGE構造体は次のように定義されています。

typedef struct _PROPSHEETPAGE { DWORD dwSize; DWORD dwFlags; HINSTANCE hInstance; union { LPCSTR pszTemplate; LPCDLGTEMPLATE pResource; }; union { HICON hIcon; LPCSTR pszIcon; }; LPCSTR pszTitle; DLGPROC pfnDlgProc; LPARAM lParam; LPFNPSPCALLBACK pfnCallback; UINT FAR * pcRefParent; #if (_WIN32_IE >= 0x0400) LPCTSTR pszHeaderTitle; LPCTSTR pszHeaderSubTitle; #endif } PROPSHEETPAGE, FAR *LPPROPSHEETPAGE;

#ifから#endifまでが新しく追加になったメンバです。

pszHeaderTitleには、ヘッダ領域のタイトルを指定します。 dwFlagsにPSP_HIDEHEADERが指定されていると無視されます。 また、dwFlagsにPSP_USEHEADERTITLEが指定されていないと無視されます。 PROPSHEETHEADER構造体のdwFlagsにPSH_WIZARD97が指定されて いなくても無視されます。

pszHeaderSubTitleには、ヘッダ領域のサブタイトルを指定します。 dwFlagsにPSP_HIDEHEADERが指定されていると無視されます。 dwFlagsにPSP_USEHEADERSUBTITLEが含まれていないと無視されます。 また、PROPSHEETHEADER構造体のdwFlagsにPSH_WIZARD97が指定されて いなくても無視されます。

では、プログラムをみてみることにします。

まず、透かしのためのビットマップリソースを用意して下さい。 ここでは、水色の小さな円を使っています。("MYBMP")

// wiz02.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "オプション(&O)" BEGIN MENUITEM "登録ウィザード(&W)", IDM_WIZ END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // PAGE1 DIALOG DISCARDABLE 0, 0, 235, 39 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "氏名の入力" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,65,20,119,12,ES_AUTOHSCROLL LTEXT "氏名を入力して下さい。",IDC_STATIC,66,7,68,8 END PAGE2 DIALOG DISCARDABLE 0, 0, 255, 43 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "住所の入力" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,85,17,100,12,ES_AUTOHSCROLL LTEXT "住所を入力して下さい。",IDC_STATIC,85,7,68,8 END PAGE3 DIALOG DISCARDABLE 0, 0, 262, 43 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "生年月日の入力" FONT 9, "MS Pゴシック" BEGIN LTEXT "生年月日を入力して下さい。",IDC_STATIC,82,10,82,8 LTEXT "西暦",IDC_STATIC,82,28,15,8 EDITTEXT IDC_EDIT1,101,24,33,12,ES_AUTOHSCROLL LTEXT "年",IDC_STATIC,138,28,8,8 EDITTEXT IDC_EDIT2,145,24,17,12,ES_AUTOHSCROLL LTEXT "月",IDC_STATIC,166,28,8,8 EDITTEXT IDC_EDIT3,178,24,17,12,ES_AUTOHSCROLL LTEXT "日",IDC_STATIC,198,28,8,8 END ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP BITMAP DISCARDABLE "header.bmp"

最後のビットマップが増えただけです。

// wiz02.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK Page1Proc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK Page2Proc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK Page3Proc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void MyPropCreate(HWND); char szClassName[] = "wiz02"; //ウィンドウクラス HINSTANCE hInst; //次の変数がウィザードによって値が代入されます。 char szName[64], szAddress[128], szYear[16], szMonth[8], szDate[8], szBirthDay[64]; //Nextボタンが押されたら仮に次の変数に値を格納しておきます //Nextボタンを押した時にszName, szAddressに値を格納すると次の //ページに移動した時にキャンセルしてもszName, szAddressの値が有効になってしまいます。 char szNamex[64], szAddressx[128];

前章に比べて一部グローバル変数に変更があります。 コメントを読んでもらえればわかると思います。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); 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; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); }

いつもと同じです。

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるウィザード97", //タイトルバーにこの名前が表示されます 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; }

ウィンドウ名がちょっと変更になりました。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; INITCOMMONCONTROLSEX ic; HDC hdc; PAINTSTRUCT ps; char szStr[512]; switch (msg) { case WM_CREATE: ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&ic); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); wsprintf(szStr, "%s, %s, %s", szName, szAddress, szBirthDay); TextOut(hdc, 10, 10, szStr, lstrlen(szStr)); EndPaint(hWnd, &ps); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_WIZ: MyPropCreate(hWnd); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

プログラムの関係上初期状態でszName, szAddress, szBirthDayに 「未記入」の文字列をコピーするのをやめました。

void MyPropCreate(HWND hWnd) { PROPSHEETPAGE psp; PROPSHEETHEADER psh; HPROPSHEETPAGE hp[3]; psp.dwSize = sizeof(PROPSHEETPAGE); psp.dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; psp.hInstance = hInst; psp.pszTemplate = "PAGE1"; psp.pfnDlgProc = (DLGPROC)Page1Proc; psp.pszHeaderTitle = "氏名の入力"; psp.pszHeaderSubTitle = "ここで氏名を入力して下さい。"; hp[0] = CreatePropertySheetPage(&psp); psp.pszTemplate = "PAGE2"; psp.pfnDlgProc = (DLGPROC)Page2Proc; psp.pszHeaderTitle = "住所の入力"; psp.pszHeaderSubTitle = "ここで住所の入力を行います。"; hp[1] = CreatePropertySheetPage(&psp); psp.pszTemplate = "PAGE3"; psp.pfnDlgProc = (DLGPROC)Page3Proc; psp.pszHeaderTitle = "生年月日の入力"; psp.pszHeaderSubTitle = "生年月日を西暦で入力します"; hp[2] = CreatePropertySheetPage(&psp); memset(&psh, 0, sizeof(PROPSHEETHEADER)); psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK; psh.hInstance = hInst; psh.hwndParent = hWnd; psh.nPages = 3; psh.phpage = hp; psh.pszbmWatermark = "MYBMP"; PropertySheet(&psh); return; }

PROPSHEETPAGE構造体とPROPSHEETHEADER構造体の 設定が前章と比べて大分変わりました。 ウォーターマーク、ヘッダタイトル、ヘッダサブタイトルの 入れ方をみて下さい。

LRESULT CALLBACK Page1Proc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { LPPSHNOTIFY lppshN; static HWND hName; char szStr[64] = ""; switch (msg) { case WM_INITDIALOG: hName = GetDlgItem(hDlg, IDC_EDIT1); Edit_SetText(hName, szName); return TRUE; case WM_NOTIFY: lppshN = (LPPSHNOTIFY)lp; switch (lppshN->hdr.code) { case PSN_SETACTIVE: PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_NEXT); break; case PSN_WIZNEXT: Edit_GetText(hName, szStr, sizeof(szStr)); if (strcmp(szStr, "") == 0) { SetFocus(hName); MessageBox(hDlg, "記入されていません", "未記入", MB_OK); SetWindowLong(hDlg, DWL_MSGRESULT, (LONG)-1); return TRUE; } else strcpy(szNamex, szStr); break; } } return FALSE; }

WM_INITDIALOGメッセージが来たらすでに設定されている 文字列をエディットコントロールに表示します。

次へボタンが押された時(PSN_WIZNEXT)エディットコントロールに何も 入力されていない時は、次のページに進みません。記入されていれば その文字列をszNamexにコピーします。(szNameではない!)

次のベーページに行かないようにするには SetWindowLong関数を用いてダイアログプロシージャ内で処理された メッセージの戻り値を−1にします。そしてTRUEを返します。

LRESULT CALLBACK Page2Proc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { LPPSHNOTIFY lppshN; static HWND hAddress; char szStr[128]; switch (msg) { case WM_INITDIALOG: hAddress = GetDlgItem(hDlg, IDC_EDIT1); Edit_SetText(hAddress, szAddress); return TRUE; case WM_NOTIFY: lppshN = (LPPSHNOTIFY)lp; switch (lppshN->hdr.code) { case PSN_SETACTIVE: PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK | PSWIZB_NEXT); break; case PSN_WIZNEXT: Edit_GetText(hAddress, szStr, sizeof(szStr)); if (strcmp(szStr, "") == 0) { SetFocus(hAddress); MessageBox(hDlg, "住所が記入されていません", "未記入", MB_OK); SetWindowLong(hDlg, DWL_MSGRESULT, -1); return TRUE; } strcpy(szAddressx, szStr); break; } } return FALSE; }

Page2Procとほぼ同様の処理をしています。

LRESULT CALLBACK Page3Proc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { LPPSHNOTIFY lppshN; static HWND hYear, hMon, hDate; char szStr1[16] = "", szStr2[8] = "", szStr3[8] = ""; HWND hMain; switch (msg) { case WM_INITDIALOG: hYear = GetDlgItem(hDlg, IDC_EDIT1); hMon = GetDlgItem(hDlg, IDC_EDIT2); hDate = GetDlgItem(hDlg, IDC_EDIT3); Edit_SetText(hYear, szYear); Edit_SetText(hMon, szMonth); Edit_SetText(hDate, szDate); return TRUE; case WM_NOTIFY: lppshN = (LPPSHNOTIFY)lp; switch (lppshN->hdr.code) { case PSN_WIZFINISH: Edit_GetText(hYear, szStr1, sizeof(szYear)); Edit_GetText(hMon, szStr2, sizeof(szMonth)); Edit_GetText(hDate, szStr3, sizeof(szDate)); if (!(strcmp(szStr1, "") * strcmp(szStr2, "") * strcmp(szStr3, ""))) { SetFocus(hYear); MessageBox(hDlg, "未記入部分があります", "未記入", MB_OK); SetWindowLong(hDlg, DWL_MSGRESULT, (LONG)-1); return TRUE; } strcpy(szYear, szStr1); strcpy(szMonth, szStr2); strcpy(szDate, szStr3); wsprintf(szBirthDay, "%s/%s/%s", szYear, szMonth, szDate); strcpy(szName, szNamex); strcpy(szAddress, szAddressx); hMain = GetParent(GetParent(hDlg)); InvalidateRect(hMain, NULL, TRUE); break; case PSN_SETACTIVE: PropSheet_SetWizButtons(GetParent(hDlg), PSWIZB_BACK | PSWIZB_FINISH); break; } } return FALSE; }

「完了」ボタンが押された時に「年」「月」「日」のどれかひとつでも 記入されていないと完了しないようにしてあります。全部記入されていると 1ページ目、2ページ目の内容をszName, szAddressにコピーして、 メインウィンドウのクライアント領域を再描画します。

今回は、ちょっとめんどうな個所がありましたが基本的には前章と 同じです。


[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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