一見すると、Microsoft Internet Explorer 5 以降のメニュー バーは標準メニューに似ています。 しかし、使い始めてみると、かなり異なって見えます。
次のスクリーン ショットは、[ツール] メニューが選択された状態の Windows Internet Explorer のメニュー バーを示しています。
このメニュー バーは、実際には標準メニューに似たツール バー コントロールです。 メニュー バーには、トップレベル メニュー項目の代わりに、一連のテキストのみのボタンがあり、クリックされるとドロップダウン メニューが表示されます。 ただし、特殊なタイプのツール バーであるメニュー バーには、標準メニューにはない機能がいくつかあります。
- 標準のツール バーの手法を使用してカスタマイズできるため、ユーザーが項目を移動、削除、または追加できます。
- ホット トラッキングを使用できるため、ユーザーは項目をクリックしなくても、トップレベル項目を選択している状態であることが分かります。
メニュー バーを Rebar コントロールに組み込むことで、以下の機能を提供できます。
- グリッパーを設定できます。これにより、ユーザーはバンドを移動またはサイズ変更できます。
- Rebar コントロール内のストリップを、前の図で示した標準のツール バーなどの他のバンドと共有できます。
- 隣接するバンドで隠れているときにシェブロンを表示し、非表示の項目にユーザーがアクセスできるようにできます。
- アプリケーションで定義する背景ビットマップを指定できます。
このトピックでは、メニュー バーを実装する方法について説明します。 メニュー バーはツール バー コントロールの特殊な実装であるため、メニュー バーに固有のトピックを重点的に説明します。 ツール バーを Rebar コントロールに組み込む方法の説明については、「Internet Explorer スタイルのツール バーを作成する方法」および「Rebar コントロールについて」を参照してください。
ツール バーをメニュー バーにする
ツール バーをメニュー バーにするには:
- 他のウィンドウ スタイル フラグと共に TBSTYLE_FLAT を含めることで、フラット ツール バーを作成します。 TBSTYLE_FLAT スタイルでは、ホット トラッキングも有効になります。 このスタイルでは、ユーザーがボタンをアクティブ化するまで、メニュー バーは標準メニューとよく似た表示です。 アクティブ化すると、ボタンはツール バーから浮き出して見え、標準のボタンと同じようにクリックすると押し下げられます。 ホット トラッキングが有効になっているため、ボタンをアクティブ化するために必要なのは、カーソルをボタンの上に重ねるだけです。 カーソルが別のボタンに移動すると、カーソルがアクティブになり、元のボタンは非アクティブになります。
- 他のウィンドウ スタイル フラグと共に TBSTYLE_LIST を含めることで、リストスタイルのボタンを作成します。 このスタイルでは、標準のトップレベル メニュー項目のように見える、幅の狭いボタンが作成されます。
- ボタンをテキストのみにするには、ボタンの TBBUTTON 構造体の iBitmap メンバーを I_IMAGENONE に設定し、iString メンバーをボタン テキストに設定します。
- 各ボタンに BTNS_DROPDOWN スタイルを設定します。 ボタンがクリックされると、ツール バー コントロールは、ボタンのメニューを表示させる TBN_DROPDOWN 通知をアプリケーションに送信します。
- メニュー バーを Rebar バンドに組み込みます。 Enable both grippers and chevrons, as discussed in 「Internet Explorer スタイルのツール バーを作成する方法」で説明されているように、グリッパーとシェブロンの両方を有効にします。
- クリックされたときにボタンのドロップダウン メニューを表示するための TBN_DROPDOWN ハンドラーを実装します。 ドロップダウン メニューは、ポップアップ メニューの一種です。 TrackPopupMenu 関数を使用することで、メニューの左上隅をボタンの左下隅に揃えた状態で作成されます。
- マウスを使わずにメニュー バーに完全にアクセスできるように、キーボード ナビゲーションを実装します。
- メニューのホット トラッキングを実装します。 標準メニューでは、トップレベル メニュー項目のメニューが表示された後にカーソルが別のトップレベル項目に移動すると、そのメニューが自動的に表示され、前の項目のメニューは折りたたまれます。 これは、ツール バー コントロールがカーソルをホット トラックしてボタンの画像を変更し、その新しいメニューを自動的に表示しています。 ご自身のアプリケーションでもそれを明示的に行う必要があります。
これらの項目のほとんどは簡単に実装できますが、別のトピックで説明されています。 ツール バーと Rebar コントロールの使い方の一般的な説明については、「Internet Explorer スタイルのツール バーを作成する方法」、「ツール バー コントロールについて」、または「Rebar コントロールについて」を参照してください。 ポップアップ メニューの処理方法の説明については、「メニュー」を参照してください。 最後の 2 つの項目であるキーボード ナビゲーションとメニューのホット トラッキングについては、このトピックの残りの部分で説明します。
メニューのホット トラッキングが無効な場合のナビゲーションの処理
ユーザーは、メニュー バー内を移動するのに、マウス、キーボード、またはその両方の組み合わせを選択できます。 メニュー バーのナビゲーションを実装するには、ご自身のアプリケーションで、ツール バー通知を処理し、マウスとキーボードを監視する必要があります。 このタスクは、メニューのホット トラッキングがある場合とない場合の 2 つの別個の部分に分けられます。 このセクションでは、メニューが表示されておらず、メニューのホット トラッキングが有効になっていない場合に、ナビゲーションを処理する方法について説明します。
マウスでのナビゲーション
メニューのホット トラッキングが無効になっている場合は、メニュー バーを通常のツール バーとして扱うことができます。 ユーザーがマウスを使用して移動している場合、アプリケーションで行う必要があるのは、TBN_DROPDOWN 通知の処理です。 この通知を受信したら、適切なドロップダウン メニューを表示し、メニューのホット トラッキングを有効にします。 それ以降は、「メニューホットトラッキングが有効になっているナビゲーションの処理」に説明されている、メニューのホット トラッキングによる管理状態になります。
「混在型ナビゲーション」で説明されているように、アクティブなボタンを追跡し続けるために TBN_HOTITEMCHANGE 通知を処理する必要もあります。 この通知は、ユーザーがマウスでのみ移動している場合は関係ありませんが、マウスとキーボードでのナビゲーションが混在する場合に必要です。
キーボード ナビゲーション
前のセクションで説明したように、メニューのホット トラッキングが無効になっているときは、ユーザーは多くのナビゲーション操作をキーボードで実行できます。 ツール バー コントロールは、フォーカスがある場合にのみキーボード ナビゲーションをサポートします。 また、メニュー バーで必要となるすべてのキー押下を処理するわけではありません。 キーボード ナビゲーションを処理する最も簡単な解決策は、関連するキー押下イベントを明示的に処理することです。
メニューのホット トラッキングが無効になっている場合、アプリケーションでは次の方法でこれらのキー押下イベントを処理する必要があります。
- F10 キー。 最初のボタンを TB_SETHOTITEM でアクティブ化します。
- 左方向キーと右方向キー。 隣接するボタンを TB_SETHOTITEM でアクティブ化します。 ユーザーがメニュー バーのいずれかの端を超えて移動しようとした場合は、反対側にある最初のボタンをアクティブ化します。
- Esc キー。 アクティブなボタンを TB_SETHOTITEM で非アクティブ化します。 メニュー バーを、最初のボタンをアクティブ化する前の状態に戻す必要があります。
- Alt キー アクセラレータ キー。 TB_MAPACCELERATOR メッセージを使用して、キー文字がどのボタンに対応しているのかを判別します。 ボタンのドロップダウン メニューを表示し、メニューのホット トラッキングを有効にします。
- ↓キー。 ボタンがアクティブであるがメニューが表示されていない場合は、ボタンのメニューを表示し、メニューのホット トラッキングを有効にします。
- Enter キー。 アクティブなボタンを TB_SETHOTITEM で非アクティブ化します。 メニュー バーを、最初のボタンをアクティブ化する前の状態に戻す必要があります。
混在型ナビゲーション
前のセクションで説明したキーボード ナビゲーション ハンドラーは、基本的に 2 つのタスクを実行します。アクティブなボタンを追跡することと、ボタンが選択されたときに適切なメニューを表示することです。 ユーザーがキーボードでのみ移動する限り、これらのハンドラーで十分です。 ただし多くの場合、ユーザーはキーボードとマウスのナビゲーションを混在させて使用します。 たとえば、F10 キーを使用して最初のボタンをアクティブ化し、次にマウスを使用して別のボタンをアクティブ化してから、下方向キーでそのメニューを開くことが考えられます。 キー押下のみを監視してアクティブなキーを追跡していると、誤ったメニューを表示することになります。 アクティブなボタンを正確に追跡するためには、TBN_HOTITEMCHANGE 通知も処理する必要があります。
メニューのホット トラッキングが有効な場合のナビゲーションの処理
メニューのホット トラッキングが有効になっているときは、アプリケーションでユーザー ナビゲーションに応答する方法を変更する必要があります。 標準メニューの動作を再現するには、以下の機能を明示的に実装する必要があります。
マウス ナビゲーションの場合:
- ユーザーがカーソルを別のボタンの上に移動した場合、そのメニューをすぐに表示し、前のメニューを消去します。
- ユーザーがコマンドを選択するか、メニュー領域に含まれていないポイントをクリックするまで、メニューのホット トラッキングをアクティブなままにします。 どちらのアクションでも、別のボタンがクリックされるまで、メニューのホット トラッキングを非アクティブ化します。
- カーソルがメニューから離れた場合は、カーソルが戻るまで、またはユーザーがメニューの対象領域の外側のポイントをクリックするまで、現在のドロップダウン メニューを表示したままにします。 現在表示されているボタンとは異なるボタンにカーソルが戻った場合は、古いメニューを折りたたんで新しいメニューを表示します。
キーボード ナビゲーションの場合:
右方向キーが押されたら、フォーカスを右に移動します。 項目にサブメニューがある場合は、サブメニューを表示します。 項目にサブメニューがない場合は、メニューとサブメニューを折りたたみ、次のボタンを TB_SETHOTITEM でアクティブ化して、その隣接するボタンのメニューを表示します。 このメッセージを受信したときに最後のボタンがアクティブである場合は、最初のボタンのメニューを表示します。
左方向キーが押されたら、フォーカスを左に移動します。 項目がサブメニューである場合は折りたたみ、フォーカスを親メニューに移動します。 項目がサブメニューではない場合は、メニューを折りたたみ、次のボタンを TB_SETHOTITEM でアクティブ化して、そのボタンのメニューを表示します。
Esc キーが押されたら、表示を 1 ステップ上に戻します。
- サブメニューが表示されている場合は、サブメニューを折りたたみ、親メニューにフォーカスを移動します。
- ボタンのメニューが表示されている場合は、メニューを折りたたみ、メニューのホット トラッキングを無効にします。 項目のボタンはアクティブなままにします。
- メニューが表示されておらず、ボタンがアクティブな場合は、ボタンを非アクティブにし、メニューのホット トラッキングを無効にします。
上方向キーと下方向キーは、特定のメニュー内を移動するためにのみ使用されます。
Enter キーが押されたら、メニュー項目に関連付けられているコマンドを起動します。 メニュー項目にサブメニューがある場合は、Enter キーが押されたらサブメニューを表示します。
メニューのホット トラッキングが無効になっている場合と同様に、アプリケーションで、マウス、キーボード、および混在型のナビゲーションを処理できる必要があります。 ただし、メニューが表示されるということは、メッセージを多少異なる方法で処理する必要があることを意味します。
メニューのホット トラッキング用のメッセージ処理
メニューのホット トラッキングでは、新しいメニューに切り替えるために必要な短い間隔を別にして、常にメニューが表示されている必要があります。 ただし、TrackPopupMenu によって表示されるドロップダウン メニューはモーダルです。 アプリケーションでは引き続き、WM_COMMAND、TBN_HOTITEMCHANGE を含む一部のメッセージと、WM_MENUSELECT などの通常のメニュー関連のメッセージを受信します。 ただし、キーボードまたはマウスの低レベルのメッセージは直接受信しません。 WM_MOUSEMOVE などのメッセージを処理するためには、メニューに送られるメッセージをインターセプトするためのメッセージ フックを設定する必要があります。
ドロップダウン メニューが表示されたら、idHook パラメーターを WH_MSGFILTER に設定して SetWindowsHookEx 関数を呼び出すことで、メッセージ フックを設定します。 そのメニュー用のすべてのメッセージが MessageProc に渡されるようになります。 たとえば、次のコード フラグメントでは、ドロップダウン メニューに送られるメッセージをトラップするメッセージ フックが設定されます。 MsgHook
はフック プロシージャの名前で、hhookMsg
はプロシージャのハンドルです。
hhookMsg = SetWindowsHookEx(WH_MSGFILTER, MsgHook, HINST_THISDLL, 0);
メニュー メッセージは、フックのプロシージャの nCode パラメーターを MSGF_MENU に設定することで識別されます。 lParam パラメーターは、メッセージを持つ MSG 構造体をポイントします。 処理する必要があるメッセージの詳細と方法については、このトピックの残りの部分で説明します。
アプリケーションでは、CallNextHookEx 関数を呼び出すことにより、すべてのメッセージを次のメッセージ フックに渡す必要があります。 また、マウス メッセージをツール バー コントロールに直接送信し、ポイント座標をツール バー クライアントの座標空間に変換する必要があります。 メッセージを直接送信することで、ツール バー コントロールが適切なマウス メッセージを確実に受け取るようにします。 たとえば、ツール バー ボタンをホット トラッキングするためには、ツール バーが WM_MOUSEMOVE メッセージを受信する必要があります。
新しいボタンがアクティブ化されたときは、アプリケーションで WM_CANCELMODE メッセージを使用して古いドロップダウン メニューを折りたたみ、新しいメニューを表示します。 また、キーボード ナビゲーションによってメニュー バーからフォーカスが移動したり、メニュー領域の外側がクリックされたりした場合は、ドロップダウン メニューを折りたたむ必要もあります。 メニューを折りたたむときは常に、UnhookWindowsHookEx 関数を使用してメッセージ フックを解放する必要があります。 別のドロップダウン メニューを表示する必要がある場合は、新しいメッセージ フックを作成します。 コマンドが起動されるとメニューは自動的に折りたたまれますが、メッセージ フックは明示的に解放する必要があります。
次のコードの例では、前の例で設定したメッセージ フックを解放します。
UnhookWindowsHookEx(hhookMsg);
マウスでのナビゲーション
通常のツール バー コントロールでボタンがホット トラッキングされているときは、新しいボタンがアクティブ化されるたびに、アクティブなボタンが強調表示され、アプリケーションに TBN_HOTITEMCHANGE 通知が送信されます。 適切なドロップダウン メニューを表示する責任は、アプリケーション側にあります。 その条件を次に示します。
- TBN_HOTITEMCHANGE 通知を処理して、アクティブなボタンを追跡します。 アクティブなボタンが変更されたら、古いメニューを折りたたんで新しいメニューを作成します。
- ボタンがクリックされたときに送信される TBN_DROPDOWN 通知を処理します。 その後、メニューを折りたたみ、メニューのホット トラッキングを無効にする必要があります。 ボタンはアクティブなままにします。
- マウスの位置を追跡するには、メッセージ フック プロシージャ内で WM_LBUTTONDOWN、WM_RBUTTONDOWN、および WM_MOUSEMOVE メッセージを処理します。 メニュー領域の外側でマウスがクリックされた場合は、現在のドロップダウン メニューを折りたたみ、メニューのホット トラッキングを非アクティブ化して、メニュー バーをアクティブ化前の状態に戻します。
- メニュー コマンドが起動されたら、メニューのホット トラッキングを無効にします。 メニューは自動的に折りたたまれますが、メッセージ フックは明示的に解放する必要があります。
メニュー項目でサブメニューを表示する必要があるときに送信される WM_INITMENUPOPUP メッセージなど、メニュー関連のメッセージも処理する必要があります。 このようなメッセージを処理する方法については、「メニュー」を参照してください。
キーボード ナビゲーション
アプリケーションでは、メッセージ フック プロシージャ内でキーボード メッセージを処理し、メニューのホット トラッキングに影響を与えるメッセージに対応する必要があります。 以下のキー押下を処理する必要があります。
- Esc キー。 Esc キーが押されたら、表示を 1 レベル上に戻します。 サブメニューが表示されている場合は、折りたたむ必要があります。 ボタンのプライマリ メニューが表示されている場合は折りたたみ、メニューのホット トラッキングを無効にします。 ボタンはアクティブなままにします。
- →キー。 項目にサブメニューがある場合は、それを表示します。 項目にサブメニューがない場合は、メニューとサブメニューを折りたたみ、次のボタンを TB_SETHOTITEM でアクティブ化して、そのメニューを表示します。 この通知を受信したときに最後のボタンがアクティブだった場合は、最初のボタンのメニューを表示します。
- ←キー。 項目がサブメニュー内にある場合は折りたたみ、フォーカスを親メニューに移動します。 項目がサブメニューではない場合は、メニューを折りたたみ、隣接するボタンを TB_SETHOTITEM でアクティブ化して、そのメニューを表示します。 この通知を受信したときに最初のボタンがアクティブだった場合は、最後のボタンのメニューを表示します。
- 上方向キーと下方向キー。 これらのキーはメニュー内を移動するために使用されますが、メニューのホット トラッキングには直接影響しません。
- Alt キー アクセラレータ キー。 TB_MAPACCELERATOR メッセージを使用して、キー文字がどのボタンに対応しているのかを判別します。 現在アクティブなボタンとは別のボタンに対応している場合は、現在のドロップダウン メニューを折りたたみ、次のボタンを TB_SETHOTITEM でアクティブ化して、その隣接するボタンのメニューを表示します。 キー文字が現在表示されているボタンに対応している場合は、ドロップダウン メニューを折りたたみ、メニューのホット トラッキングを無効にします。 ボタンはアクティブなままにします。