テクニカル ノート 61: ON_NOTIFY メッセージと WM_NOTIFY メッセージ
更新 : 2007 年 11 月
メモ : |
---|
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。 |
このテクニカル ノートでは、新しい WM_NOTIFY メッセージの背景と MFC (Microsoft Foundation Class) アプリケーションにおける WM_NOTIFY メッセージの推奨される (そして最も一般的な) 処理方法を説明します。
Windows 3.x における通知メッセージ
Windows 3.x では、コントロールは親に対してメッセージを送信することで、マウス クリック、内容や選択項目の変化、コントロールの背景の描画などのさまざまなイベントを通知します。単純な通知は特殊な WM_COMMAND メッセージとして送信され、wParam にパックされた通知コード (BN_CLICKED など) およびコントロール ID と lParam にパックされたコントロールのハンドルを伴います。wParam と lParam はすべて使用されているため、追加データを渡す方法はありません。これらのメッセージは単純な通知であることが必要です。たとえば、BN_CLICKED 通知でボタンがクリックされた時点で、マウス カーソルの位置に関する情報は送信できません。
Windows 3.x のコントロールは、追加データを含む通知メッセージを送信する必要がある場合には、WM_CTLCOLOR、WM_VSCROLL、WM_HSCROLL、WM_DRAWITEM、WM_MEASUREITEM、WM_COMPAREITEM、WM_DELETEITEM、WM_CHARTOITEM、WM_VKEYTOITEM など、さまざまな専用のメッセージを使用します。これらのメッセージは送信元のコントロールにリフレクションできます。詳細については、「テクニカル ノート 62: Windows コントロールへのメッセージ リフレクション (メッセージ返送)」を参照してください。
Win32 における通知メッセージ
Windows 3.1 に存在していたコントロールに対しては、Win32 API は Windows 3.x で使用されていたほとんどの通知メッセージを使用します。ただし、Win32 では Windows 3.x でサポートされていたコントロール以外に、数多くの洗練された複雑なコントロールが追加されています。これらのコントロールの多くは、追加データおよび通知メッセージを送信する必要があります。Win32 API の設計では、追加データが必要な新しい通知のすべてに新規の WM_* メッセージを追加する代わりに、WM_NOTIFY というメッセージを 1 つだけ追加して、標準の方法で任意の数の追加データを渡せるようにしました。
WM_NOTIFY メッセージでは、メッセージを送信するコントロールの ID を wParam に、構造体へのポインタを lParam に持たせます。この構造体は、NMHDR 構造体か、または NMHDR 構造体を最初のメンバに持つようなより大きな構造体のいずれかです。NMHDR メンバが最初にあるため、この構造体へのポインタはキャストの方法によって、NMHDR へのポインタとより大きな構造体へのポインタのどちらにでも使用できます。
ほとんどの場合、このポインタはより大きな構造体を指定しており、キャストして使用する必要があります。NMHDR 構造体が使用されるのは、共通の通知 (名前が NM_ で始まる通知) やツール ヒント コントロールの TTN_SHOW 通知と TTN_POP 通知などに限られます。
NMHDR 構造体または最初のメンバには、メッセージを送信するコントロールのハンドルおよび ID と通知コード (TTN_SHOW など) が含まれます。NMHDR 構造体の形式は次のとおりです。
typedef struct tagNMHDR {
HWND hwndFrom;
UINT idFrom;
UINT code;
} NMHDR;
TTN_SHOW メッセージに関しては、code メンバは TTN_SHOW に設定されます。
ほとんどの通知は、NMHDR 構造体を最初のメンバに持つ、より大きな構造体へのポインタを渡します。たとえば、リスト ビュー コントロールでキーを押したときに送信される LVN_KEYDOWN 通知メッセージが使用する構造体があるとします。ポインタは LV_KEYDOWN 構造体を指します。この構造体は次のように定義されています。
typedef struct tagLV_KEYDOWN {
NMHDR hdr;
WORD wVKey;
UINT flags;
} LV_KEYDOWN;
この構造体の最初のメンバが NMHDR なので、通知メッセージで渡されるポインタを NMHDR へのポインタと LV_KEYDOWN へのポインタのどちらにもキャストできます。
新しい Windows コントロールすべてに共通の通知
通知には、新しい Windows コントロールのすべてに共通のものがあります。これらの通知は NMHDR 構造体へのポインタを渡します。
通知コード |
送信理由 |
---|---|
NM_CLICK |
ユーザーがコントロールで左ボタンをクリックした。 |
NM_DBLCLK |
ユーザーがコントロールで左ボタンをダブルクリックした。 |
NM_RCLICK |
ユーザーがコントロールで右ボタンをクリックした。 |
NM_RDBLCLK |
ユーザーがコントロールで右ボタンをダブルクリックした。 |
NM_RETURN |
ユーザーがコントロールが入力フォーカスを持っているコントロールで Enter キーを押した。 |
NM_SETFOCUS |
コントロールが入力フォーカスを得た。 |
NM_KILLFOCUS |
コントロールが入力フォーカスを失った。 |
NM_OUTOFMEMORY |
メモリ不足のためコントロールが処理を完了できなかった。 |
ON_NOTIFY : MFC アプリケーションにおける WM_NOTIFY メッセージの処理
CWnd::OnNotify 関数は通知メッセージを処理します。既定の実装では、メッセージ マップをチェックして、呼び出す通知ハンドラを見つけます。一般に、OnNotify のオーバーライドは行いません。その代わり、ハンドラ関数を定義して、オーナー ウィンドウのクラスのメッセージ マップにそのハンドラのメッセージ マップ エントリを追加します。
ClassWizard プロパティ シートを通じて、ClassWizard で ON_NOTIFY のメッセージ マップ エントリを作成し、スケルトンのハンドラ関数を使用できます。この作業に関する ClassWizard の使用方法の詳細については、「関数へのメッセージの割り当て」を参照してください。
ON_NOTIFY メッセージマップ マクロの構文は以下のとおりです。
ON_NOTIFY(wNotifyCode,id,memberFxn)
イタリック体で示されたパラメータの意味は以下のとおりです。
wNotifyCode
処理すべき通知メッセージのコード (たとえば LVN_KEYDOWN)。id
通知の送信先であるコントロールの子の識別子。memberFxn
通知の送信時に呼び出されるメンバ関数。
このメンバ関数は次のプロトタイプ宣言を持つ必要があります。
afx_msgvoidmemberFxn(NMHDR*pNotifyStruct,LRESULT*result);
解説
イタリック体で示されたパラメータの意味は以下のとおりです。
pNotifyStruct
上述の、通知の構造体へのポインタ。result
戻る前に設定する結果コードへのポインタ。
使用例
メンバ関数 OnKeydownList1 を使って ID が IDC_LIST1 の CListCtrl からの LVN_KEYDOWN メッセージを処理するには、ClassWizard を使って次のようにメッセージ マップに追加します。
ON_NOTIFY( LVN_KEYDOWN, IDC_LIST1, OnKeydownList1 )
この例では、ClassWizard によって提供される関数は次のとおりです。
void CMessageReflectionDlg::OnKeydownList1(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR;
// TODO: Add your control notification handler
// code here
*pResult = 0;
}
ClassWizard によって、適切な型のポインタが自動的に用意されます。通知の構造体には、pNMHDR または pLVKeyDow を使用してアクセスできます。
ON_NOTIFY_RANGE
同じ WM_NOTIFY メッセージを複数のコントロールの集まりについて処理する必要がある場合は、ON_NOTIFY の代わりに ON_NOTIFY_RANGE を使用できます。たとえば、特定の通知メッセージに対して同じ動作を実行させるボタンの集まりがある場合です。
ON_NOTIFY_RANGE を使う場合、通知メッセージを処理すべき子の識別子の連続した範囲の始まりと終わりを指定します。
ClassWizard は ON_NOTIFY_RANGE を処理しないので、メッセージ マップを手作業で編集する必要があります。
ON_NOTIFY_RANGE のメッセージマップ エントリと関数プロトタイプは以下のとおりです。
ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )
イタリック体で示されたパラメータの意味は以下のとおりです。
wNotifyCode
処理すべき通知メッセージのコード (たとえば LVN_KEYDOWN)。id
連続する範囲の識別子の始まり。idLast
連続する範囲の識別子の終わり。memberFxn
通知の送信時に呼び出されるメンバ関数。
このメンバ関数は次のプロトタイプ宣言を持つ必要があります。
afx_msgvoidmemberFxn(UINT id, NMHDR*pNotifyStruct,LRESULT*result);
解説
イタリック体で示されたパラメータの意味は以下のとおりです。
id
通知を送信したコントロールの子の識別子。pNotifyStruct
上述の、通知の構造体へのポインタ。result
戻る前に設定する結果コードへのポインタ。
ON_NOTIFY_EX、ON_NOTIFY_EX_RANGE
通知のルーティング内で複数のオブジェクトにメッセージを処理させる場合は、ON_NOTIFY (または ON_NOTIFY_RANGE) の代わりに ON_NOTIFY_EX (または ON_NOTIFY_EX_RANGE) を使用できます。EX バージョンと普通のバージョンの唯一の違いは、EX バージョンに対して呼び出されるメンバ関数の戻り値が、メッセージ処理を継続する必要があるかどうかを示す BOOL 値であることです。この関数が FALSE を返す場合、同じメッセージを複数のオブジェクトで処理できます。
ClassWizard は ON_NOTIFY_EX や ON_NOTIFY_EX_RANGE を処理しないので、これらのいずれかを使う場合、メッセージ マップを手作業で編集する必要があります。
ON_NOTIFY_EX と ON_NOTIFY_EX_RANGE のメッセージ マップ エントリと関数プロトタイプは以下のとおりです。パラメータの意味は、EX の付かないバージョンと同様です。
ON_NOTIFY_EX(nCode,id,memberFxn)ON_NOTIFY_EX_RANGE( wNotifyCode, id, idLast,memberFxn )
どちらもプロトタイプは同じです。
afx_msgBOOLmemberFxn(UINT id, NMHDR*pNotifyStruct,LRESULT*result);
解説
いずれの場合も、id は通知を送信したコントロールの子の識別子を保持します。
このメンバ関数の戻り値は、通知メッセージが完全に処理された場合には TRUE であり、コマンド ルーティング中の他のオブジェクトでこのメッセージを処理できるようにする場合には FALSE を返す必要があります。