ウィザードを作成する方法
[アーティクル] 2024/03/05
7 人の共同作成者
フィードバック
この記事の内容
知っておくべきこと
手順
解説
関連トピック
ウィザードとはプロパティ シートの一種であり、手順をユーザーに解説するための簡単かつ強力な手段です。
ウィザードは、ユーザー エクスペリエンスを簡素化するための重要な要素の 1 つです。 これにより、アプリケーションの構成などの複雑な操作を実行し、操作を一連の簡単な手順に分割できます。 プロセスの各ポイントで、何が必要か説明し、ユーザーが選択を行ってテキストを入力できるコントロールを表示できます。
ウィザードは、実際にはプロパティ シートの一種です。 プロパティ シートは基本的に "ページ" のコレクションのコンテナーであり、各ページは個別のダイアログ ボックスです。 通常のプロパティ シートでは、ユーザーはいつでも任意のページにアクセスできますが、ウィザードではページが順番に表示されます。 前方と後方のページへの移動には、タブではなくボタンが使用されます。 ページの表示順序はアプリケーションによって制御され、ユーザー入力に基づいて変更できます。
ウィザードには、古い Wizard97 スタイルと Windows Vista で導入された Aero スタイルの 2 つのメイン スタイルがあります。 実際の図については、「プロパティ シートの概要 」を参照してください (3 つ目のスタイルは PSH_WIZARD フラグまたは PSH_WIZARD_LITE フラグのみを使用し、ヘッダーや透かしのないプロパティ シートの単純なシーケンスを示します)。
注意
ウィザードのコンテキストにおける "透かし" とは、一部のページの左余白に表示されるビットマップです。
このドキュメントのほとんどの説明では、バージョン 5.80 以降のコモン コントロールを使用するシステム向けにウィザードを実装することを前提としています。 それより前のバージョンのコモン コントロールで Wizard97 スタイルを使用しようとすると、アプリケーションはコンパイルされる可能性がありますが、正しく表示されません。 以前のシステムで Wizard97 互換ウィザードを作成する方法については、このトピックで後述する「下位互換性ウィザード」を参照してください。
C/C++
Windows ユーザー インターフェイス プログラミング
ウィザードの実装手順は、通常のプロパティ シートを実装する場合と似ています。 最も基本的なレベルでは、プロパティ シートを定義する PROPSHEETHEADER 構造体で、次のいずれかのフラグまたはフラグの組み合わせを設定します。
テーブルを展開する
フラグ
スタイル
PSH_WIZARD
ヘッダーやビットマップがない単純なウィザード。
PSH_WIZARD_LITE
PSH_WIZARD に似ていますが、外観に若干の違いがあります。たとえば、ボタンの上の区切り線はウィンドウの全幅に設定されます。
PSH_WIZARD97
(オプションの) ヘッダー、ヘッダー ビットマップ、透かしがある Wizard97 ウィザード。
PSH_WIZARD | PSH_AEROWIZARD
Aero ウィザード。 Aero ウィザードでは、透かしやヘッダー ビットマップは使用されません。 シングル スレッド アパートメント (STA) モデルが必要です。
ウィザードを実装するための基本的な手順は次のとおりです。
各ページのダイアログ ボックス テンプレートを作成します。
各ページの PROPSHEETPAGE 構造体を作成して、ページを定義します。 この構造体はページを定義し、ダイアログ ボックス テンプレートとビットマップまたはその他のリソースへのポインターを含みます。
前の手順で作成した PROPSHEETPAGE 構造体を CreatePropertySheetPage 関数に渡して、ページの HPROPSHEETPAGE ハンドルを作成します。
PROPSHEETHEADER 構造体を作成して、ウィザードを定義します。
PROPSHEETHEADER 構造体を PropertySheet 関数に渡して、ウィザードを表示します。
各ページのダイアログ ボックス プロシージャを実装して、ページのコントロールやウィザードのボタンからの通知メッセージの処理や、他の Windows メッセージングの処理を行います。
ウィザード ページには、外部と内部という 2 つの基本的な種類があります。 外部ページは、導入 (ようこそ) ページと完了ページです。 その他はすべて、内部ページです。
外部ページのダイアログ ボックス テンプレート
導入ページと完了ページの基本的なレイアウトは同じです。 次の図は、サンプルの Wizard97 の導入ページを示しています。このページでは、プレースホルダーの透かしが使用されています。
Wizard97 の外部ページの場合、ダイアログ ボックス テンプレートは 317 x 193 ダイアログ単位です。 キャプションと下部のバンド ([戻る] ボタン、[次へ] ボタン、[キャンセル] ボタンを含む) を除き、ウィザード全体に表示されます。 "透かし" ビットマップ用に用意されているテンプレートの左側には、コントロールを含めることはできません。 透かしはウィザードの PROPSHEETHEADER 構造体で指定され、ページに自動的に追加されます。 リソース テンプレートを設計するときは、そのための領域を確保する必要があります。
透かしビットマップの作成時は、たとえばユーザーが大きなシステム フォントを選択した場合に、ダイアログ ボックスのサイズが大きくなる可能性があることに注意してください。 また、言語によってフォント メトリックが異なる傾向があります。 ページが大きくなると、透かし専用の領域も比例して大きくなります。 ただし、透かしビットマップを変更したり、ビットマップを大きい領域に合わせて拡大したりすることはできません。 代わりに、ビットマップは専用領域の左上部分に元のサイズのまま表示されます。 透かしが適用されない大きな専用領域の部分は、ビットマップの左上のピクセルの色で自動的に塗りつぶされます。
フォント メトリックごとに異なるサイズの透かしビットマップが必要な場合は、次の 2 つの解決策が考えられます。
ウィザードを作成する前にフォント メトリックを取得し、適切なサイズの透かしビットマップを指定します。
ウィザードの作成時に透かしビットマップを指定しないでください。 Wizard97 では、透かし領域が空白のままになります。 次に、透かし専用の領域に適切なサイズのビットマップを描画します。
通常のダイアログ ボックスの場合と同様に、透かしの右側の領域にコントロールを配置できます。 この領域の背景色はシステムによって決定されるため、特に操作は必要ありません。 通常、この領域には 2 つの静的コントロールを配置します。 上のコントロールではタイトルを表示し、大きなボールド フォント (Wizard97 の場合は 12 ポイントの Verdana Bold) を使用します。 もう 1 つは説明テキスト用で、標準のダイアログ ボックス フォントを使用します。
導入ページと完了ページの主な違いは、ウィザードのボタンと静的コントロールのテキストです。 通常、導入ページには [次へ] ボタンと [戻る] ボタンがあり、[次へ] ボタンのみが有効になっています。 完了ページでは [戻る] ボタンが有効になっていて、[次へ] ボタンが [完了] ボタンに置き換えられています。
注意
Aero ウィザードでは、[戻る] ボタンがキャプション バーの矢印ボタンに置き換えられます。
[完了] ボタンのテキストを変更するには、ウィザードに PSM_SETFINISHTEXT メッセージを送信します。 既定では、[完了] ボタンにはキーボード アクセラレータは含まれません。 キーボード アクセラレータを定義するには、PSM_SETFINISHTEXT に渡すテキスト文字列にアンパサンドを含めます。 たとえば、"&Finish" は 'F' をキーボード アクセラレータとして定義します。
内部ページのダイアログ ボックス テンプレート
内部ページの外観は、外部ページとは若干異なります。 次の図は、サンプルの Wizard97 の内部ページを示しています。このページでは、プレースホルダーのヘッダー ビットマップが使用されています。
ページ上部のヘッダー領域はプロパティ シートによって処理されるため、テンプレートには含まれません。 ヘッダーの内容は、ページの PROPSHEETPAGE 構造体とウィザードの PROPSHEETHEADER 構造体で指定されます。 内部ページはヘッダーとボタンの間に収まる必要があるため、Wizard97 ダイアログ ボックス テンプレートは 317 x 143 ダイアログ単位で、外部ページのテンプレートよりもやや小さくなります。
次の図は、同じテンプレートから作成された Aero ウィザードを示しています。
ダイアログ ボックス テンプレートと、ビットマップや文字列テーブルなどの関連リソースを作成したら、プロパティ シートのページを作成できます。 この手順は、標準プロパティ シートの場合と同様です。 まず、PROPSHEETPAGE 構造体の適切なメンバーを入力します (一部のメンバーはウィザードに固有です)。次に、CreatePropertySheetPage 関数を呼び出して、ページの HPROPSHEETPAGE ハンドルを作成します。
次のウィザード関連のフラグは、PROPSHEETPAGE 構造体の dwFlags メンバーで設定できます。
テーブルを展開する
フラグ
説明
PSP_HIDEHEADER
Wizard97 の外部ページにこのフラグを設定します。 ヘッダーは表示されず、透かしを表示できます。
PSP_USEHEADERTITLE
内部ページにこのフラグを設定して、タイトルを Wizard97 のヘッダー領域、または Aero ウィザードのクライアント領域の上部に配置します。
PSP_USEHEADERSUBTITLE
内部ページにこのフラグを設定して、Wizard97 のヘッダー領域にサブタイトルを配置します。
PSP_USEHEADERTITLE または PSP_USEHEADERSUBTITLE を設定した場合は、タイトルとサブタイトルのテキストを pszHeaderTitle メンバーと pszHeaderSubtitle メンバーにそれぞれ代入します。 テキスト文字列を PROPSHEETPAGE 構造体と PROPSHEETHEADER 構造体のメンバーに代入する場合は、文字列ポインターを代入するか、MAKEINTRESOURCE マクロを使用して文字列リソースから値を代入できます。 文字列リソースは、ウィザードの PROPSHEETHEADER 構造体の hInstance メンバーで指定されたモジュールから読み込まれます。
CreatePropertySheetPage を呼び出してページを作成する場合は、結果を HPROPSHEETPAGE ハンドルの配列の要素に代入します。 この配列は、プロパティ シートの作成時に使用されます。 ページのハンドルの配列インデックスによって、ページが表示される既定の順序が決まります。 ページの HPROPSHEETPAGE ハンドルを作成したら、同じ PROPSHEETPAGE 構造体を再利用して次のページを作成するために、関連するメンバーに新しい値を代入します。
ページを作成する別の方法として、ページごとに個別の PROPSHEETPAGE 構造体を使用し、構造体の配列を作成できます。 この配列は、プロパティ シートの作成時に HPROPSHEETPAGE ハンドルの配列の代わりに使用されます。 個別の PROPSHEETPAGE 構造体を使用すると、CreatePropertySheetPage を呼び出す必要がなくなりますが、使用されるメモリは増えます。 それ以外に 2 つのアプローチに大きな違いはありません。
次の例では、PROPSHEETPAGE 構造体に値を代入して、内部 Wizard97 ページを定義します。 この例では、ページのタイトル、サブタイトル、ダイアログ ボックス テンプレートはすべて、そのリソース ID によって識別されます。 次に、CreatePropertySheetPage 関数が呼び出され、ページの HPROPSHEETPAGE ハンドルが作成されます。 2 番目のページが表示されるため、ハンドルはハンドルの配列である ahpsp に代入され、インデックスは 1 になります。
// g_hInstance is the global HINSTANCE of the application.
// IntPage1DlgProc is the dialog procedure for this page.
// ahpsp is an array of HPROPSHEETPAGE handles.
PROPSHEETPAGE psp = { sizeof(psp) };
psp.hInstance = g_hInstance;
psp.dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
psp.lParam = (LPARAM) &wizdata;
psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_TITLE1);
psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_SUBTITLE1);
psp.pszTemplate = MAKEINTRESOURCE(IDD_INTERIOR1);
psp.pfnDlgProc = IntPage1DlgProc;
ahpsp[1] = CreatePropertySheetPage(&psp);
ページの作成時に、PROPSHEETPAGE 構造体の lParam メンバーを使用してページにカスタム データを割り当てることができます。通常これを行うには、ユーザー定義の構造体へのポインターを割り当てます。
ページが最初に選択されると、そのダイアログ ボックス プロシージャは WM_INITDIALOG メッセージを受け取ります。 このメッセージの lParam 値は、ページの PROPSHEETPAGE 構造体のコピーを指し、そこからカスタム データを取得できます。 次に、このデータを後続のメッセージで使用するために保管できます。そのためには、インデックス パラメーターとして GWL_USERDATA を指定した SetWindowLongPtr を使用します。 複数のページに同じデータへのポインターを設定でき、1 つのページで行われたデータの変更は、ダイアログ プロシージャ内の他のページで使用できます。
通常のプロパティ シートと同様に、PROPSHEETHEADER 構造体のメンバーを入力してウィザードのプロパティ シートを定義します。 この構造を使用すると、ウィザードを構成するページと、ページが表示される既定の順序を、いくつかの関連パラメーターと共に指定できます。 次に、PropertySheet 関数を呼び出してウィザードを起動します。
Wizard97 スタイルでは、 PROPSHEETHEADER 構造体の pszCaption メンバーは無視されます。 代わりに、現在のページのダイアログ ボックス テンプレートで指定されているキャプションがウィザードに表示されます。 テンプレートにキャプションがない場合は、前のページのキャプションが表示されます。 したがって、すべてのページに同じキャプションを表示するには、導入ページのテンプレートでキャプションを指定します。
Aero Wizard スタイルでは、ダイアログ ボックスのキャプションは pszCaption から取得されます。
ページに HPROPSHEETPAGE ハンドルの配列を作成した場合は、その配列を phpage メンバーに代入します。 代わりに、PROPSHEETPAGE 構造体の配列を作成した場合は、その配列を ppsp メンバーに代入し、dwFlags メンバーで PSH_PROPSHEETPAGE フラグを設定します。
次の例では、psh (PROPSHEETHEADER 構造体) に値を代入し、PropertySheet 関数を呼び出してウィザードを起動します。 Wizard97 スタイルのウィザードには、リソース ID で指定される透かしとヘッダー グラフィックの両方があります。 ahpsp 配列にはすべての HPROPSHEETPAGE ハンドルが含まれ、それらが表示される既定の順序が定義されます。
// g_hInstance is the global HINSTANCE of the application.
// ahpsp is an array of HPROPSHEETPAGE handles.
PROPSHEETHEADER psh = { sizeof(psh) };
psh.hInstance = g_hInstance;
psh.hwndParent = NULL;
psh.phpage = ahpsp;
psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER;
psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
psh.pszbmHeader = MAKEINTRESOURCE(IDB_BANNER);
psh.nStartPage = 0;
psh.nPages = 4;
PropertySheet(&psh);
ウィザードの各ページには、Windows メッセージ (特にコントロールとウィザードからの通知) を処理するためのダイアログ ボックス プロシージャが必要です。 ほぼすべてのウィザードで処理できる必要がある 3 つのメッセージは、WM_INITDIALOG 、WM_DESTROY 、WM_NOTIFY です。
WM_NOTIFY メッセージは、ページが表示される前に、ウィザードのいずれかのボタンがクリックされたときに受信します。 メッセージの lParam パラメーターは、NMHDR ヘッダー構造体へのポインターです。 通知の ID は、構造体の code メンバーに含まれています。 ほとんどのウィザードで処理する必要がある 4 つの通知は次のとおりです。
テーブルを展開する
WM_INITDIALOG と WM_DESTROY を処理する
ページが初めて表示される間際に、そのダイアログ ボックス プロシージャは WM_INITDIALOG メッセージを受け取ります。 このメッセージを処理すると、カスタム データの保存やフォントの設定などの必要な初期化タスクをウィザードで実行できるようになります。
プロパティ シートが破棄されると、WM_DESTROY メッセージを受け取ります。 ウィザードはシステムによって自動的に破棄されますが、このメッセージを処理すると、必要なクリーンアップを実行できます。
PSN_SETACTIVE 通知コードは、ページが表示されようとするたびに送信されます。 ページに初めてアクセスされると、WM_INITDIALOG メッセージに続いて PSN_SETACTIVE が送信されます。 ページへのその後のアクセスでは、PSN_SETACTIVE 通知のみを受け取ります。 この通知は通常、ページのデータを初期化し、適切なボタンを有効化するために処理されます。
既定では、ウィザードには [戻る] ボタン、[次へ] ボタン、[キャンセル] ボタンが表示され、すべてのボタンが有効になっています。 ボタンを無効にしたり、[次へ] の代わりに [完了] を表示したりするには、PSM_SETWIZBUTTONS メッセージを送信する必要があります。 このメッセージが送信されると、ボタンの状態は、新しいページが選択された場合でも、別の PSM_SETWIZBUTTONS メッセージによって変更されるまで保持されます。 通常、すべての PSN_SETACTIVE ハンドラーはこのメッセージを送信して、各ページのボタンを正しい状態に維持します。
このメッセージを使用して、いつでもボタンの状態を変更できます。 たとえば、[次へ] ボタンを最初に無効にすることができます。 ユーザーが必要なすべての情報を入力したら、[次へ] ボタンを有効化してユーザーが次のページに進めるように、別の PSM_SETWIZBUTTONS メッセージを送信できます。
次のコード フラグメントでは、PropSheet_SetWizButtons マクロを使用して、内部ページが表示される前に、そのぺージの [戻る] ボタンと [次へ] ボタンを有効化します。
case WM_NOTIFY :
{
LPNMHDR pnmh = (LPNMHDR)lParam;
switch(pnmh->code)
{
...
case PSN_SETACTIVE :
...
// This is an interior page.
PropSheet_SetWizButtons(hwnd, PSWIZB_NEXT | PSWIZB_BACK);
...
}
...
}
PSN_WIZNEXT、PSNWIZBACK、PSN_WIZFINISH を処理する
[次へ] ボタンまたは [戻る] ボタンがクリックされると、PSN_WIZNEXT または PSN_WIZBACK の通知コードを受け取ります。 既定では、プロパティ シートの作成時に定義された順序で、ウィザードは次のページまたは前のページに自動的に移動します。 これらの通知を処理する一般的な理由は、ユーザーがページを切り替えないようにするため、または既定のページ順序をオーバーライドするためです。
ユーザーがページを切り替えないようにするには、ボタン通知を処理し、DWL_MSGRESULT 値を –1 に設定して SetWindowLong 関数を呼び出して、TRUE を返します。 次に例を示します。
case PSN_WIZNEXT :
...
// Do not go to the next page yet.
SetWindowLong(hwnd, DWL_MSGRESULT, -1);
return TRUE;
...
標準の順序をオーバーライドして特定のページに移動するには、DWL_MSGRESULT 値をページのダイアログ ボックスリソース ID に設定して SetWindowLong を呼び出し、TRUE を返します。 次に例を示します。
case PSN_WIZNEXT :
...
// Go straight to the completion page.
SetWindowLong(hwnd, DWL_MSGRESULT, IDD_FINISH);
return TRUE;
...
[完了] ボタンまたは [キャンセル] ボタンがクリックされると、PSN_WIZFINISH または PSN_RESET の通知コードをそれぞれ受け取ります。 これらのボタンのどちらかがクリックされると、ウィザードはシステムによって自動的に破棄されます。 ただし、ウィザードが破棄される前にクリーンアップ タスクを実行する必要がある場合は、これらの通知を処理できます。 PSN_WIZFINISH 通知を受け取ったときにウィザードが破棄されないようにするには、DWL_MSGRESULT 値を TRUE に設定して SetWindowLong を呼び出し、TRUE を返します。 次に例を示します。
case PSN_WIZFINISH :
...
// Not finished yet.
SetWindowLong(hwnd, DWL_MSGRESULT, TRUE);
return TRUE;
...
前のセクションは、バージョン 5 以降のコモン コントロールを使用するシステム向けにウィザードを実装することを前提としています。
それより前のバージョンのコモン コントロールを使用するシステム向けにウィザードを作成する場合、前のセクションで説明した機能の多くは使用できません。 Wizard97 スタイルで使用される PROPSHEETHEADER 構造体と PROPSHEETPAGE 構造体の多くのメンバーは、コモン コントロールのバージョン 5 以降でのみサポートされます。 ただし、Wizard97 スタイルと同様の外観の "下位互換性" ウィザードを実装することはできます。 そのためには、以下を明示的に実装する必要があります。
導入ページと完了ページのダイアログ ボックス テンプレートに透かしグラフィックを追加します。
すべてのテンプレートを同じサイズにします。 内部ページに個別のシステム定義ヘッダー領域はありません。
テンプレートで、内部ページのヘッダー領域を明示的に作成します。
ヘッダー グラフィックは、ウィザードのサイズが変更された場合にタイトルまたはサブタイトルと競合する可能性があるため、使用しないでください。
下位互換性ウィザードの詳細については、下位互換性ウィザード 97 に関する記事を参照してください。
Wizard97 の設計上の問題の詳細については、Windows SDK の他の場所にある Wizard97 仕様 を参照してください。 このドキュメントでは、ダイアログ ボックスのサイズ、ビットマップのサイズと色、コントロールの配置などに関するガイドラインを提供しています。
プロパティ シートの使用
Windows コモン コントロールのデモ (CppWindowsCommonControls)