次の方法で共有


テクニカル ノート 61: ON_NOTIFY メッセージと WM_NOTIFY メッセージ

更新 : 2007 年 11 月

749htf6k.alert_note(ja-jp,VS.90).gifメモ :

次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。

このテクニカル ノートでは、新しい 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_CTLCOLORWM_VSCROLLWM_HSCROLLWM_DRAWITEMWM_MEASUREITEMWM_COMPAREITEMWM_DELETEITEMWM_CHARTOITEMWM_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_EXON_NOTIFY_EX_RANGE を処理しないので、これらのいずれかを使う場合、メッセージ マップを手作業で編集する必要があります。

ON_NOTIFY_EXON_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 を返す必要があります。

参照

その他の技術情報

番号順テクニカル ノート

カテゴリ別テクニカル ノート