【Visual C++ 2008】【MFC】
「MFCアプリケーションウィザード」にて「ダイアログベース」のアプリケーションを生成した場合、 そのアプリケーションの終了方法には以下のパターンがある。
操作 | 終了時に通るCDialogメンバ関数 | DoModalの戻り値 | 終了コード |
---|---|---|---|
[OK]ボタン (IDOK) | OnOK | 0x1 (IDOK) | 0x0 |
[Enter]キー (IDOKボタン無し、IDCANCELボタンにフォーカス無し) |
OnOK | 0x1 (IDOK) | 0xD (VK_RETURN) |
[キャンセル]ボタン (IDCANCEL) | OnCancel | 0x2 (IDCANCEL) | 0x0 |
[システムメニュー]-[閉じる] | OnCancel | 0x2 (IDCANCEL) | 0x2 |
[システムメニュー]ダブルクリック | OnCancel | 0x2 (IDCANCEL) | 0x2 |
[X]ボタン | OnCancel | 0x2 (IDCANCEL) | 0x2 |
Alt+F4 | OnCancel | 0x2 (IDCANCEL) | 0x2 |
[Esc]キー | OnCancel | 0x2 (IDCANCEL) | 0x1B (VK_ESCAPE) |
[Esc]キー押下でアプリケーション終了してしまうのは、Windowsの作法として違和感がある。 おそらく、通常のダイアログ(ポップアップ)の作法を引きずっているためだろう。 そこで、[Esc]キーをアクセラレータ(ショートカットキー)として設定して、押下しても何も反応しないようにする。
(1)
リソースビューにて、アクセラレータを追加する(例えばIDR_ACCELERATOR1)。
IDは任意(例えばID_ESC)、修飾子は「なし」、キーは「VK_ESCAPE」。
もしくは、rcファイルを直接書き換えるなら、以下を追加。
IDR_ACCELERATOR1 ACCELERATORS
BEGIN
VK_ESCAPE, ID_ESC, VIRTKEY, NOINVERT
END
(2)
ダイアログクラスのクラスヘッダのprivateに、メンバ定義を追加。
HACCEL m_hAccel;
virtual BOOL PreTranslateMessage(MSG* pMsg);
(3)
OnInitDialogの「// TODO: 初期化をここに追加します。」より後にて、アクセラレータを読み込む。
m_hAccel = LoadAccelerators(AfxGetResourceHandle(),
MAKEINTRESOURCE(IDR_ACCELERATOR1));
(4)
メンバ関数(CWnd::PreTranslateMessageのオーバーライド)を記述。
BOOL CXxxxDlg::PreTranslateMessage(MSG* pMsg)
{
if (::TranslateAccelerator(GetSafeHwnd(), m_hAccel, pMsg))
{
// アクセラレータの場合、こちらを通る
return TRUE;
}
return CDialog::PreTranslateMessage(pMsg);
}
単に[Esc]キーを無視するだけなら、ここまででよい。 [Esc]キーに何らかの動作を割り当てる場合、引き続き(5)以降も記述する。
(5)
ダイアログクラスのクラスヘッダのprivateに、メンバ関数定義を追加。
例えば関数名をOnEscとする場合、
afx_msg void OnEsc();
(6)
メッセージマップに、ON_COMMAND(ID_ESC, OnEsc)を追加。
これにより、キーのIDと、その行き先が結びつく。
(7)
メンバ関数を記述。
void CXxxxDlg::OnEsc()
{
// [Esc]キーの動作を、ここに記述。
// 何もしない場合は、returnのみ。
return;
}
[Esc]キー以外のキーをアクセラレータに設定する場合は、(1)にそのキーを追加して、そのキー用の(5)~(7)を実装する。
ただし、冒頭に挙げた表のIDOKに対応する操作、および、IDCANCELに対応する操作にアプリケーション終了前の動作を追加する場合は、 それぞれCDialog::OnOK、CDialog::OnCancelのオーバーライドのみでよい。 アクセラレータの設定は不要。 メッセージマップには、デフォルトで内部的に以下の定義がされていると考えると、わかりやすい。(実際は記述不要)
ON_COMMAND(IDOK, OnOK)
ON_COMMAND(IDCANCEL, OnCancel)
[Esc]キーもデフォルトではIDCANCELに対応する操作であるが、アクセラレータ設定が優先される。
(メモ)
- [Enter]キーで終了しないようにするには、IDOKボタンを削除して、OnOKのオーバーライドにてreturn返しする (他のボタンにフォーカスが無い場合)
- CDialogはCWndの派生クラス
- MSDNライブラリの「CDialog::OnOK」「CDialog::OnCancel」「CWnd::PreTranslateMessage」「TranslateAccelerator」「LoadAccelerators」を参照のこと