ウィザード


初心者に愛の手を

ウィザードとは、本来「魔法使い」を意味する英語です
コンピュータの世界では、稀に見る天才的技術者をウィザードと呼んだりもします
ジョブズと共にアップルを設立したウォズニアックのことを、オズの魔法使いにかけて
ウィザード・オブー・ウォズ(ウォズの魔法使い)と読んだりするのは代表例でしょうか…

Windows では、一定の手順を踏まえて設定する
特別なプロパティシートをウィザードと呼んでいます
ウィザードは、プロパティシートのページを決まった順番で表示させる特殊なコントロールです

ウィザードを用いれば、システムについて知識の低いユーザーでも
設定漏れがないように確実に必要な情報を入力させることができます
これは、インストーラや、ソフトウェアの初期設定に用いられます

ウィザードを用いるには PROPSHEETHEADER 構造体のフラグで
PSH_WIZARD を設定しなければいけません
これを設定すれば、ページ配列の昇順にウィザードが表示されます

ウィザードには「次へ」と「戻る」ボタン、そして「キャンセル」ボタンの3つがあります
「次へ」を押せば、現在のページから次のページがアクティブになり
「戻る」を押せば、一つ前のページがアクティブになります

通常、ウィザードの最後のページになれば、設定の終了を意味するボタンを表示します
これは PropSheet_SetFinishText() マクロで通知することができます

VOID PropSheet_SetFinishText(HWND hPropSheetDlg , LPTSTR lpszText);

hPropSheetDlg には、プロパティシートのハンドルを指定します
lpszText は、「完了」ボタンの新しいテキストへのポインタを指定します

これを呼び出せば、完了ボタンが表示され、「次へ」と「戻る」ボタンが非表示になります
このマクロを呼び出すべきタイミングは、最後の一つ前のページのプロシージャでしょう

ウィザードのページでボタンが押された時も、WM_NOTIFY が発行されます
このとき、「次へ」ボタンを押すと PSN_WIZNEXT 通知コードが
「戻る」ボタンを押すと PSN_WIZBACK 通知コードが
「完了」ボタンを押すと PSN_WIZFINISH 通知コードが送られます
/*リソーススクリプト*/
PAGEICON ICON "test.ico"

KITTY DIALOG 0 , 0 , 150 , 70
FONT 16 , "MS Sans Serif"
CAPTION "Kitty's Page" {
	LTEXT "Rena is very cute, isn't she?" , -1 , 2 , 0 , 100 , 10
}

TARUTO DIALOG 0 , 0 , 150 , 70
FONT 16 , "MS Sans Serif"
CAPTION "TARUTO's Page" {
	LTEXT "Taruto is very cute, isn't she?" , -1 , 2 , 0 , 100 , 10
}

END DIALOG 0 , 0 , 150 , 70
FONT 16 , "MS Sans Serif"
CAPTION "Finish?" {
	LTEXT "We must love Nekomimi girl!" , -1 , 2 , 0 , 100 , 10
}
#include <windows.h>
#include <commctrl.h>
#define TITLE TEXT("Kitty on your lap")

BOOL CALLBACK PageProc1(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	static int iCount = 0;

	switch (msg) {
	case WM_NOTIFY:
		switch (((NMHDR *)lp)->code) {
		case PSN_WIZNEXT:
			iCount++;
			break;
		case PSN_WIZBACK:
			if (iCount != 0) iCount--;
			break;
		case PSN_WIZFINISH:
			iCount = 0;
			break;
		}
		if (iCount == 2)
			PropSheet_SetFinishText(GetParent(hWnd) , TEXT("完了(&F)"));
		return TRUE;
	}			
	return FALSE;
}

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	PROPSHEETPAGE psp;
	PROPSHEETHEADER psh;
	HPROPSHEETPAGE hPsp[3];

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		InitCommonControls();		
		return 0;
	case WM_RBUTTONUP:
		psp.dwSize = sizeof (PROPSHEETPAGE);
		psp.dwFlags = PSP_DEFAULT | PSP_USEICONID;
		psp.pszIcon = TEXT("PAGEICON");
		psp.hInstance = (HINSTANCE)GetWindowLong(hWnd , GWL_HINSTANCE);

		psp.pszTemplate = TEXT("KITTY");
		psp.pfnDlgProc = (DLGPROC)PageProc1;
		hPsp[0] = CreatePropertySheetPage(&psp);

		psp.pszTemplate = TEXT("TARUTO");
		hPsp[1] = CreatePropertySheetPage(&psp);

		psp.pszTemplate = TEXT("END");
		hPsp[2] = CreatePropertySheetPage(&psp);

		psh.dwSize = sizeof (PROPSHEETHEADER);
		psh.dwFlags = PSH_DEFAULT | PSH_USEHICON | PSH_WIZARD;
		psh.hwndParent = hWnd;
		psh.hIcon = LoadIcon(NULL , IDI_ASTERISK);
		psh.pszCaption = TITLE;
		psh.nPages = 3;
		psh.phpage = hPsp;
		PropertySheet(&psh);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


このプログラムは、リソースエディタで3つのダイアログを作成しています
ウィンドウのクライアント領域を右クリックすれば、それらをページとしてウィザードが表示され
「次へ」ボタンを押していけば、ページが移行し、最終的に「完了」ボタンが表示されます

全てのダイアログが同一のプロシージャを共有するのは好ましくありませんが
このプログラムにおいては、問題がないので PageProc1() を共有しています
「次へ」が押されると、静的なカウンタ変数が加算されていき
最後のページになったことが確認されれば、PropSheet_SetFinishText() を呼び出します


PropSheet_SetFinishText()

VOID PropSheet_SetFinishText(HWND hPropSheetDlg , LPTSTR lpszText);

「完了」ボタンのテキストを設定し
「次へ」と「戻る」ボタンを非表示にする

hPropSheetDlg - プロパティシートのハンドルを指定します
lpszText - 「完了」ボタンの新しいテキストへのポインタを指定します



前のページへ戻る次のページへ