レガシー Windows API(概要)について

ここで紹介しているAPIは、Windows95、Windows NT3.5時代のAPI群であり、最新のAPIは含まれていないことに注しして欲しい。なお、当時の解説書は現在よりもより詳細に解説しているため、参考になるはずである。

  • レイアウトを変換する際、崩れているページがあります。
  • 概要およびSampleコードはありますが、APIの解説はありません。

ご要望があれば旧版のWin32 API(HELP形式)をお譲りします。問い合わせフォームより申し込みください。

Windows API メニューの概要

「メニュー」とは、 アプリケーション コマンドのリストです。テキスト文字列やビットマップで表現される各コマンドは、 「メニュー項目」と呼ばれます。ユーザーは、 マウスやキーボードでメニュー項目を選択することによって、 そのメニュー項目に対応するコマンドをアプリケーションに実行させることができます。ユーザーがメニュー項目を選択すると、 Microsoft(R) Windows(TM) は、 ユーザーが選択したメニュー項目を示すコマンド メッセージをアプリケーションに送ります。

以下に示すトピックでは、 Win32(R) 用のアプリケーションでのメニューの使い方について説明します。[>>]ボタンを使って以下に示すトピックを順番に読んでください。または、 各トピックをクリックすると、 そのトピックの内容を表示できます。

メニュー バーとポップアップ メニュー

メニュー ハンドル

メニュー項目

キーボードによるメニューへのアクセス

メニューの作成

メニューの表示

メニューの破棄

メニュー メッセージ

メニューの修正

メニュー テンプレート リソースの使用

フローティング ポップアップ メニューの作成

メニュー項目ビットマップの使用

オーナー描画メニュー項目の作成

カスタムのチェック マーク ビットマップの使用

メニュー関数とメッセージ

 

 

メニュー バーとポップアップ メニュー

Microsoft(R) Windows(TM) 用に作成されたアプリケーションのメニューは、 階層的に配置されます。「メニュー バー」(トップ レベル メニューとも呼ばれます) は、 この階層構造のいちばん上に位置します。この下のレベルは、 「ポップアップ メニュー」(サブメニューとも呼ばれます) で構成されます。

メニュー バーの項目は、 アプリケーションが用意するコマンドのおおまかな分類を示しています。通常、 メニュー バーから項目を選択すると、 ポップアップ メニューがアクティブになります。このポップアップ メニューの各項目は、 メニュー バーの項目が示す種類のコマンドに対応しています。たとえば、 メニュー バーに[ファイル]項目があり、 ユーザーがその項目を選択すると、 [新規作成][開く][上書き保存]などのメニュー項目を持つポップアップ メニューがアクティブになります。

メニュー バーは、 オーバーラップ ウィンドウかポップアップ ウィンドウにしか付加できません。子ウィンドウにメニュー バーを付加することはできません。メニュー バーは常に表示されます。ウィンドウにタイトル バーがあるときは、 Windowsは、 タイトル バーの下にメニュー バーを置きます。これに対して、 ポップアップ メニューは、 ポップアップ メニューをアクティブ化するメニュー項目をユーザーが選択するまで表示されません。ポップアップ メニューは、 タイトル バーに隣接して表示されます。

どのメニューにも、 オーナー ウィンドウがなければなりません。ユーザーがメニューを選択したり、 メニューのコマンドを選択すると、 Windowsは、 メニューのオーナー ウィンドウにメッセージを送ります。このようなメッセージについては、 メニュー メッセージで説明します。

 

フローティング ポップアップ メニュー

Windowsは、 「フローティング ポップアップ メニュー」と呼ばれる特殊なポップアップ メニューを用意しています。フローティング ポップアップ メニューは、 メニュー バーには付加されず、 画面上の任意の位置に表示できます。通常、 アプリケーションは、 クライアント領域などのウィンドウの一部やアイコンなどの特定のオブジェクトにフローティング ポップアップ メニューを関連付けます。このため、 フローティング ポップアップ メニューはコンテキスト メニューとも呼ばれます。

フローティング ポップアップ メニューは、 ユーザーが (画面上の適切な位置を右マウス ボタンでクリックするなどして) アクティブにするまで表示されません。通常、 フローティング ポップアップ メニューは、 キャレットやマウス カーソルの位置に表示されます。

 

コントロール メニュー

「コントロール メニュー」(システム メニューとも呼ばれます) とは、 主にオペレーティング システムが定義して管理するポップアップ メニューです。コントロール メニューは、 タイトル バーの左端に表示される水平線を含む小さなボックスで表現されます。ユーザーがこのボックスをクリックすると、 Windowsは、 コントロール メニューを表示します。また、 Windowsは、 アイコン化したアプリケーションのアイコンをユーザーがクリックしたときにもコントロール メニューを表示します。

コントロール メニューには、 ウィンドウのサイズや位置の変更、 アプリケーションのクローズ、 [アプリケーションの切り替え]ダイアログ ボックスのアクティブ化などをユーザーが行うための標準メニュー項目のセットがあります。システムメニューの項目は追加、 削除、 修正できますが、 通常は、 標準メニュー項目のセットを使います。

オーバーラップ ウィンドウやポップアップ ウィンドウ、 子ウィンドウには、 コントロール メニューを付加できます。通常、 オーバーラップ ウィンドウやポップアップ ウィンドウには、 コントロール メニューを付加します。

ユーザーがコントロール メニューからコマンドを選択すると、 Windowsは、 メニューのオーナー ウィンドウにWM_SYSCOMMANDメッセージを送ります。通常、 アプリケーションのウィンドウ プロシージャは、 コントロール メニューからのメッセージを処理せずに、 DefWindowProc関数に渡すだけです。この関数は、 メッセージのシステム デフォルト処理を行います。コントロール メニューにコマンドを追加しているときは、 ウィンドウ プロシージャはそのコマンドを処理しなければなりません。

 

 

 

メニュー ハンドル

システムは、 各メニューについて、 一意なハンドルを生成します。「メニュー ハンドル」は、 HMENU型の値です。多くのWindowsメニュー関数には、 メニュー ハンドルを指定しなければなりません。アプリケーションは、 メニューを作成したりメニューをロードすると、 メニュー ハンドルを受け取ります。メニューの作成やロードについて詳しくは、 メニューの作成を参照してください。

すでに作成またはロードしてあるメニューのハンドルを取得するには、 GetMenu関数やGetSubMenu関数を使います。GetMenuは、 指定されたウィンドウに設定されているメニュー バーのハンドルを返します。GetSubMenuは、 ポップアップ項目に関連付けられたポップアップ メニューのハンドルを返します。ウィンドウのコントロール メニューのハンドルを取得するには、 GetSystemMenu関数を使います。

 

 

 

メニュー項目

それぞれのメニューには1つ以上のメニュー項目が含まれます。ユーザーはメニュー項目を選択することにより、 コマンドを実行し、 ポップアップ メニューをオープンできます。いろいろな種類のメニュー項目と、 アプリケーションがメニュー項目の外観と機能を制御する方法を以下に説明します。

 

コマンド項目とポップアップ項目

メニュー項目を選択すると、 コマンドを実行するか、 またはポップアップ メニューがオープンします。コマンドを実行するメニュー項目は「コマンド項目」と呼び、 ポップアップ メニューをオープンするものは「ポップアップ 項目」と呼びます。

ユーザーがコマンド項目を選択すると、 Windowsは、 メニューを所有するウィンドウにコマンド メッセージを送ります。コマンド項目がシステム メニューにある場合は、 Windowsは、 WM_SYSCOMMAN メッセージを送ります。それ以外の場合は、 WM_COMMAN メッセージを送ります。

それぞれのポップアップ項目には、 対応するポップアップ メニューのハンドルが関連付けられています。ユーザーがポップアップ項目を選択すると、 Windowsはポップアップ メニューをオープンします。オーナー ウィンドウにはコマンド メッセージは送られません。しかし、 Windowsはポップアップ メニューを表示する前に、 オーナー ウィンドウにWM_INITMENUPOPUP メッセージを送ります。GetSubMenu 関数を使って、 ポップアップ項目に関連付けられたポップアップ メニューのハンドルを取得できます (コマンド項目の場合は、 ハンドルはNULLです)。

メニュー バーは、 通常はポップアップ項目を含みますが、 コマンド項目を含む場合もあります。ポップアップ メニューは、 通常はコマンド項目を含みますが、 ポップアップ項目を含む場合もあります。ポップアップ メニューにポップアップ項目を追加することにより、 ポップアップ メニューを任意の深さにネストできます。ポップアップ メニューにポップアップ項目を入れた場合、 視覚的にユーザーにわかりやすいように、 Windowsはメニュー項目のテキストの右に小さい矢印を自動的に表示します。

 

メニュー項目識別子

各コマンド項目には、 識別子と呼ぶ16ビットの整数が関連付けられています。アプリケーションでは、 各メニュー項目に一意の識別子を割り当てる必要があります。コマンド項目が選択されると、 WM_COMMANDメッセージまたはWM_SYSCOMMANDメッセージの一部として、 項目の識別子がオーナー ウィンドウに送られます。また、 この識別子を使って、 メニュー関数を多数呼び出す場合に、 メニュー項目を指定することができます。

コマンド項目と同じように、 ポップアップ項目にも識別子があります。しかし、 ポップアップ項目が選択されても、 Windowsはコマンド メッセージを送らずに、 ポップアップ項目に関連付けられているポップアップ メニューをアクティブ化します。

GetMenuItemIDを使って、 指定した位置のメニュー項目の識別子を取得できます。

 

メニュー項目のチェックとチェック解除

メニュー項目は、 チェックを付加できます。メニュー項目がチェックされているときは、 Windowsは、 ビットマップを使って、 メニュー項目の左側にチェック マークを表示します。メニューがチェック解除されているときは、 Windowsは、 チェック マークを表示しません。チェック マークは、 ポップアップ メニューの項目にしか付加できません。メニュー バーのメニュー項目にチェックを付加することはできません。

通常、 アプリケーションは、 現在有効なメニュー オプションを示すのにチェック マークを使います。たとえば、 ワード プロセッサ アプリケーションでユーザーが段落書式オプションをメニューから選択すると、 アプリケーションは、 選択されたオプションにチェック マークを付加します。

チェック マーク属性は、 メニュー項目がチェックされるかどうかを示します。メニューのチェック マーク属性を設定するには、 CheckMenuItem関数を使います。メニューが現在チェックされているかどうかを判断するには、 GetMenuState関数を使います。

Windowsは、 チェック マークを表示するときに使われるデフォルトのビットマップを用意しています。しかし、 このビットマップをほかのビットマップで置き換えて、 そのビットマップを使うようにWindowsに指示することもできます。ただし、 新しいチェック マーク ビットマップの寸法は、 デフォルトのチェック マーク ビットマップと同じでなければなりません。デフォルトの寸法を取得するには、 GetMenuCheckMarkDimensions関数を使います。

各メニュー項目には、 チェック状態とチェック解除状態の2つのビットマップを関連付けることができます。メニュー項目にビットマップを関連付けるには、 SetMenuItemBitmaps関数を使います。

チェック マーク ビットマップは、 ビットマップ リソースとして用意するか、 グラフィック デバイス インターフェイス (GDI) 関数を使って実行時に作成します。しかし、 ビットマップ リソースではさまざまなビデオの解像度やアスペクト比に対応できないため、 ビットマップを適切にサイズ変更しなければなりません。チェック マーク ビットマップはモノクロまたはカラーにできます。しかし、 カラー画面では、 メニュー項目を強調表示するときにビットマップの色を反転するため、 望ましくない結果になる場合があります。ビットマップについて詳しくは、 ビットマップの概要を参照してください。

 

メニュー項目の使用可能、 使用不能、 灰色表示

メニュー項目は、 使用可能、 使用不能、 または灰色表示にできます。デフォルトでは、 メニュー項目は使用可能になっています。使用可能なメニュー項目をユーザーが選択すると、 Windowsは、 メニュー項目がポップアップ項目かどうかによって、 オーナー ウィンドウにコマンド メッセージを送るか、 対応するポップアップ メニューを表示します。

ユーザーが利用できないメニュー項目は、 使用不能か灰色表示にしてください。使用不能のメニュー項目や灰色表示のメニュー項目は、 選択できません。通常、 利用不可能なメニュー項目は灰色表示して、 コマンドを利用できないことをユーザーに示します。使用不能のメニュー項目は、 その項目を利用できないことを示すためのものではなく、 関連するメニュー項目のグループの見出しとして使われます (たとえば、 使用不能の[上書き保存]メニュー項目は、 [ファイルの保存][すべて保存][名前を付けて保存]など一連の使用可能なコマンド項目の見出しにできます)。灰色表示の項目は、 その動作が不適切なときに使います (たとえば、 システムにプリンタがインストールされていなければ、 [ファイル]メニューの[印刷]コマンドは灰色表示にします)。

メニュー項目を使用可能、 使用不能、 灰色表示にするには、 EnableMenuItem関数を使います。

 

メニュー項目の強調表示

Windowsは、 ユーザーが選択したメニュー項目を自動的に強調表示します。しかし、 HiliteMenuItem関数を呼び出して、 強調表示を強制的に設定または解除できます。この関数は、 ポップアップ メニューの項目には効果はありません。また、 HiliteMenuItemを使ってメニュー項目を強調表示しても、 メニュー項目は選択されているように見えるだけです。ユーザーがEnterキーを押しても、 強調表示されている項目は選択されません。

 

文字列、 ビットマップ、 オーナー描画メニュー項目

アプリケーションは文字列やビットマップを使って、 メニュー項目を表示するよう指定できます。さらに、 アプリケーションは、 「オーナー描画項目」を使って、 メニュー項目の外観を完全に制御できます。オーナー描画項目を使う場合は、 選択 (強調表示)、 チェック、 チェック解除の状態をアプリケーションがすべて行わなければなりません。

たとえば、 フォント メニューを使う場合、 各メニュー項目を対応するフォントで描画できます。ローマンの項目はローマン フォントで、 イタリックの項目はイタリック フォントで描画できます。

 

メニュー項目のセパレータと改行

Windowsは、 水平線で構成されるセパレータという特殊なメニュー項目を用意しています。セパレータ メニュー項目は、 ポップアップ メニューを関連する項目ごとに分割するのに使います。メニュー バーではセパレータ項目は使えません。ユーザーはセパレータ項目を選択できません。

1行に収まらない個数のメニュー項目がメニュー バーにある場合、 Windowsは、 メニューを自動的に複数行に分割します。また、 アプリケーションは、 項目にMF_MENUBREAKフラグを指定して、 メニュー バーの特定の項目で改行させることができます。Windowsは、 その項目から後の項目を次の行に置きます。

1列には収まらない個数の項目がポップアップ メニューにある場合、 Windowsはメニューを自動的に複数列に分割します。また、 アプリケーションは、 項目にMF_MENUBREAKフラグを指定して、 ポップアップ メニューの特定の項目を次の列に置くことができます。MF_MENUBREAKフラグを項目に設定すると、 Windowsは、 その項目から後の項目を次の列に置きます。MF_MENUBARBREAKフラグも同様ですが、 このフラグの場合、 Windowsは、 縦棒を使ってメニューの列どうしを区切ります。

 

 

 

キーボードによるメニューへのアクセス

Windowsは、 メニュー用の標準キーボード インターフェイスを用意しています。メニュー項目にニーモニックやアクセラレータ キー (アクセス キーともいう) を指定することによって、 このインターフェイスを拡張できます。ここでは、 標準キーボード インターフェイスと、 ニーモニックやアクセラレータ キーについて説明します。各トピックをクリックすると、 そのトピックの内容を表示できます。

 

標準キーボード インターフェイス

ニーモニック

キーボード アクセラレータ

 

標準キーボード インターフェイス

Windowsは、 マウスなどのポインティング デバイスがなくても使えるように設計されています。Windowsは標準キーボード インターフェイスを用意しているため、 ユーザはキーボードを使ってメニュー項目を選択できます。このキーボード インターフェイスをサポートするには、 特別なコードは不要です。キーボードかマウスでメニュー項目が選択されると、 アプリケーションはコマンド メッセージを受け取ります。標準キーボード インターフェイスは、 次に示すキーストロークを処理します。

 

キーストローク 動作

Altキー メニュー バー モードを切り換えます。

Alt+Spaceキー コントロール メニューを表示します。

Escキー メニュー モードを終了します。

キー 次のトップ レベル メニュー項目に移動します。トップ レベル メニュー項目には、 メニュー バー項目とシステム メニューが含まれます。選択されている項目がポップアップ メニュー内にあるときは、 ポップアップ メニューの次の列を選択するか、 次のトップ レベル メニュー項目を選択します。

キー キーの反対の動作をします。ポップアップ メニューでは、 1列前に移動します。現在選択されている項目がいちばん左の列にあるときは、 1つ前のポップアップ メニューを選択します。

キーまたはキー トップ レベル メニューで押されたときは、 ポップアップ メニューをアクティブ化します。ポップアップ メニュー内で押されたときは、 次または前のメニュー項目を選択します。

Enterキー ポップアップ メニューをアクティブ化し、 項目にポップアップ メニューが関連付けられていれば、 その最初の項目を選択します。そうでないときは、 ユーザーが項目を選択してマウス ボタンを放したときと同様に、 項目を選択します。

英字キー 指定された文字をニーモニックに持つ最初のメニュー項目を選択します。選択された項目にポップアップ メニューが関連付けられているときは、 そのメニューを表示し、 最初の項目を強調表示します。そうでないときは、 項目を選択します。

 

ニーモニック

メニュー項目にニーモニックを追加することによって、 メニューの標準キーボード インターフェイスを拡張できます。ニーモニックとは、 メニュー項目のテキストの下線が付いている文字です。メニューがアクティブならば、 ユーザーは、 メニュー項目の下線付き文字に対応するキーを押して項目を選択できます。ユーザーがAltキーを押して離すとメニュー バーがアクティブになり、 メニュー バーの最初の項目が強調表示されます。ポップアップ メニューは、 表示されているときはアクティブです。

メニュー項目のニーモニック キーストロークを指定するには、 項目のテキスト文字列の対応する文字の前にアンパサンド (&) を付加します。たとえば、 “&Move”と指定すると、 Windowsは、 “M”に下線を付加します。

 

キーボード アクセラレータ

メニュー項目には、 ニーモニックのほかに、 キーボード アクセラレータを付加することもできます。キーボード アクセラレータは、 ニーモニックと異なり、 メニューをアクティブにしなくても使えます。また、 ニーモニックには必ずメニュー項目が対応しているのに対し、 キーボード アクセラレータはメニュー項目に関連付ける必要はありません。

キーボード アクセラレータを識別するテキストは、 メニュー項目のテキスト文字列に付加します。アクセラレータ テキストは、 メニュー項目名の右側に、 タブの後に指定します。たとえば、 “&Close\tAlt+F4”は、 キーボード アクセラレータのAlt+F4キーの組み合わせとニーモニックのCを持つ[Close]項目を表します。アクセラレータについて詳しくは、 キーボード アクセラレータの概要を参照してください。

 

 

メニューの作成

アプリケーションのメニューを作成するには、 メニュー テンプレートまたはメニュー作成関数を使います。メニュー テンプレートは、 通常はリソースとして定義されています。メニュー テンプレートのリソースは明示的にロードするか、 またはウィンドウ クラスのデフォルト メニューとして割り当てることができます。また、 メニュー テンプレートのリソースは、 メモリ内で動的に作成することもできます。

 

メニュー テンプレート リソース

通常、 メニューは、 メニュー テンプレートを作成しておいて実行時にロードすることによって実現します。メニュー テンプレートは、 メニューと、 メニュー バーやポップアップ メニューの項目を定義します。メニュー テンプレート リソースの作成について、 詳しくはツールのドキュメントを参照してください。

メニュー テンプレート リソースを作成し、 アプリケーションの実行可能 (.EXE) ファイルに追加したら、 LoadMenu関数を使って、 メモリにリソースをロードします。この関数はメニューのハンドルを返します。その後、 SetMenu関数を使って、 このハンドルをウィンドウに割り当てます。

メニューをリソースとして実現することによって、 アプリケーションのローカライズが簡単になります。変換するのはリソース定義ファイルだけで、 アプリケーションのソース コードは変換する必要はありません。

 

メモリ内のメニュー テンプレート

メニューは、 実行時にメモリに構築したメモリ テンプレートからも作成できます。たとえば、 ユーザーがメニューをカスタマイズできるアプリケーションでは、 ユーザーの選択に基づいてメモリ内にメニュー テンプレートを作成する場合があります。その後、 アプリケーションはファイルまたはレジストリにテンプレートを保存しておき、 将来使うことができます。メモリ内のテンプレートからメニューを作成するには、 LoadMenuIndirect関数を使います。

 

メニュー作成関数

メニュー作成関数を使って、 実行時にメニューを作成し、 既存のメニューにメニュー項目を追加できます。CreateMenuは、 空のメニュー バーを作成します。CreatePopupMenuは、 空のポップアップ メニューを作成します。メニューを作成したら、 AppendMenu関数かInsertMenu関数を使って、 メニュー項目を追加します。

 

 

メニューの表示

メニューをロードまたは作成したら、 ウィンドウを表示する前にそのウィンドウにメニューを設定しなければなりません。ウィンドウにメニューを設定するには、 クラス メニューを定義します。クラス メニューについて詳しくは、 クラス メニューを参照してください。CreateWindowEx関数のhmenuパラメータにメニューのハンドルを指定するか、 またはSetMenu関数を呼び出して、 ウィンドウにメニューを割り当てることもできます。

フローティング ポップアップ メニューを表示するには、 TrackPopupMenu関数を使います。

 

 

クラス メニュー

ウィンドウ クラスを登録するとき、 そのクラスのデフォルト メニューを指定できます。このデフォルト メニューは、 「クラス メニュー」とも呼ばれます。クラス メニューを指定するには、 クラスを登録したときに使ったWNDCLASS構造体のlpszMenuNameメンバに、 メニュー テンプレート リソースの名前を割り当てます。

それぞれのウィンドウには、 ウィンドウ クラスに対してクラス メニューがデフォルトで割り当てられます。このため、 メニューを明示的にロードしてそれぞれのウィンドウに割り当てる必要はありません。CreateWindowEx関数を呼び出すときに、 別のメニュー ハンドルを指定すれば、 クラス メニューを無効にできます。SetMenu関数を使えば、 ウィンドウのメニューを作成したあとで変更できます。ウィンドウ クラスについて詳しくは、 ウィンドウ クラスを参照してください。

 

 

 

メニューの破棄

メニューを設定したウィンドウが破棄されると、 Windowsは、 そのメニューを自動的に破棄し、 メニューのハンドルとメニューに使われていたメモリを解放します。Windowsは、 ウィンドウに設定されていないメニューは自動的には破棄しません。ウィンドウに設定していないメニューは、 DestroyMenu関数を呼び出して破棄してください。メニューを破棄しないと、 アプリケーションをクローズしてもメニューはメモリ内に残ります。

 

 

メニュー メッセージ

Windowsは、 メニューを所有するウィンドウのウィンドウ プロシージャにメッセージを送って、 メニューに関する情報を報告します。ユーザーがフローティング ポップアップ メニューを表示するために、 メニュー バーの項目を選択するか、 またはマウスの右ボタンをクリックすると、 Windowsは一連のメッセージを送ります。

ユーザーがメニュー バーの項目をアクティブにすると、 オーナー ウィンドウは最初にWM_SYSCOMMANDメッセージを受け取ります。このメッセージには、 フラグが含まれていて、 ユーザーがメニューをアクティブ化するためにキーボード (SC_KEYMENU) を使ったか、 またはマウス (SC_MOUSEMENU) を使ったかを示します。メニューのキーボード インターフェイスについて、 詳しくはキーボードによるメニューへのアクセスを参照してください。

次に、 Windowsは、 ポップアップ メニューを表示する前にWM_INITMENUメッセージをウィンドウ プロシージャに送り、 メニューをユーザーが見る前にアプリケーションから修正できるようにします。Windowsは、 メニューをアクティブ化するごとに、 WM_INITMENUメッセージを一度だけ送ります。

ユーザーがポップアップ項目を選択すると、 Windowsは、 ポップアップ メニューを表示する前に、 WM_INITMENUPOPUPメッセージをオーナー ウィンドウに送ります。このメッセージによって、 アプリケーションは、 ポップアップ メニューが表示される前にもう一度ポップアップ メニューを修正できます。

ユーザーが項目の強調表示を移動すると、 Windowsは、 メニューのオーナー ウィンドウのウィンドウ プロシージャにWM_MENUSELECTメッセージを送ります。このメッセージは、 現在選択されているメニュー項目を識別します。アプリケーションは、 このメッセージを使って、 選択されているメニュー項目に関する説明をメイン ウィンドウのいちばん下の情報領域に表示できます。

ユーザーがメニューからコマンド項目を選択すると、 Windowsは、 WM_COMMANDメッセージをウィンドウ プロシージャに送ります。WM_COMMANDメッセージのwParamパラメータの下位ワードは、 選択された項目の識別子を示します。ウィンドウ プロシージャは、 識別子を調べて、 メッセージを適切に処理しなければなりません。

 

 

メニューの修正

メニューをロードまたは作成した後で修正する関数には、 さまざまなものがあります。これには、 メニュー項目の追加や削除、 既存のメニュー項目の変更が含まれます。

メニューからメニュー項目を削除するには、 DeleteMenu関数やRemoveMenu関数を使います。削除する項目がポップアップ項目ならば、 DeleteMenuは、 関連するポップアップ メニューを削除し、 メニュー ハンドルを破棄して、 ポップアップ メニューに使われていたメモリを解放します。RemoveMenu関数はメニュー項目を削除しますが、 項目にポップアップ メニューが関連付けられているときはポップアップ メニューやハンドルを破棄しないため、 メニューを後で再利用できます。

メニュー バーを修正したら、 DrawMenuBar関数を呼び出してメニュー バーを再描画してください。再描画しなければ、 Windowsがオーナー ウィンドウを再描画するまで修正は反映されません。

 

 

 

 

メニューの使用

この節では、 次を説明します。

メニュー テンプレート リソースの使用

フローティング ポップアップ メニューの作成

メニュー項目ビットマップの使用

オーナー描画メニュー項目の作成

カスタムのチェック マーク ビットマップの使用

 

 

 

メニュー テンプレート リソースの使用

通常、 アプリケーションにメニューを付加するには、 メニュー テンプレート リソースを作成し、 実行時にメニューをロードします。次の、 メニュー テンプレート リソースのロードでは、 メニュー テンプレート リソースのロード方法と使用法について説明します。メニュー テンプレート リソースの作成について、 詳しくは、 開発ツールに含まれているドキュメントを参照してください。

 

メニュー テンプレート リソースのロード

アプリケーションのメニュー テンプレート リソースをロードするには、 リソースを含むモジュールのハンドルとメニュー テンプレートの識別子を指定して、 LoadMenu関数を呼び出します。LoadMenuは、 ウィンドウにメニューを設定するために使うメニュー ハンドルを返します。このウィンドウは、 メニューのオーナー ウィンドウになり、 メニューが生成したメッセージをすべて受け取ります。

ウィンドウにメニューを設定するには、 SetMenu関数を使うか、 ウィンドウを作成するときにCreateWindowEx関数のhmenuパラメータにメニューのハンドルを指定します。また、 ウィンドウ クラスを登録するときにメニュー テンプレートを指定してウィンドウにメニューを設定することもできます。この方法では、 メニューはそのウィンドウ クラスのクラス メニューになります。そのクラスのウィンドウを作成すると、 Windowsは、 指定されたメニューをそのウィンドウに自動的に設定します。

クラス メニューを作成するには、 WNDCLASS構造体のlpszMenuNameメンバにメニュー テンプレート リソースの識別子を設定し、 その構造体のアドレスをRegisterClass関数に渡します。

 

クラス メニューの作成

次の例は、 アプリケーションのクラス メニューを作成する方法、 そのクラス メニューを使うウィンドウを作成する方法、 ウィンドウ プロシージャでメニュー コマンドを処理する方法を示しています。

 

HINSTANCE hinst;

int APIENTRY WinMain(hinstance, hPrevInstance, lpCmdLine, nCmdShow)
HINSTANCE hinstance;
HINSTANCE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
MSG msg; /* message */
WNDCLASS wc; /* window-class data */
HWND hwnd; /* handle of main window */

/*
* Create the window class for the main window. Specify
* the identifier of the menu-template resource as the
* lpszMenuName member of the WNDCLASS structure to create
* the class menu.
*/

wc.style = 0;
wc.lpfnWndProc = (WNDPROC) MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MyMenuResource";
wc.lpszClassName = "MainWClass";

if (!RegisterClass(&wc))
return FALSE;

hinst = hinstance;

/*
* Create the main window. Set the hmenu parameter to NULL so
* that Windows uses the class menu for the window.
*/

hwnd = CreateWindow("MainWClass", "Sample Application",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinstance,
NULL);

if (hwnd == NULL)
return FALSE;

/*
* Make the window visible and send a WM_PAINT message to the
* window procedure.
*/

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

/* Start the main message loop. */

while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
UNREFERENCED_PARAMETER(hPrevInstance);
}


LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

switch (uMsg) {
.
. /* Process other window messages. */
.

case WM_COMMAND:

/* Test for the identifier of a command item. */

switch(LOWORD(wParam)) {
case IDM_FI_OPEN:
DoFileOpen(); /* application-defined */
break;

case IDM_FI_CLOSE:
DoFileClose(); /* application-defined */
break;
.
. /* Process other menu commands. */
.

default:
break;

}
return 0;

.
. /* Process other window messages. */
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}

 

 

フローティング ポップアップ メニューの作成

フローティング ポップアップ メニューを使うには、 ポップアップ メニューのハンドルをTrackPopupMenu関数に渡します。通常、 TrackPopupMenuは、 WM_LBUTTONDOWNWM_KEYDOWNなどのユーザーが生成したメッセージに対して、 ウィンドウ プロシージャから呼び出します。

TrackPopupMenuには、 ポップアップ メニュー ハンドルのほかに、 オーナー ウィンドウのハンドル、 (スクリーン座標系での) フローティング ポップアップ メニューの位置、 ユーザーが項目の選択に使うマウス ボタンを指定しなければなりません。

位置を指定するには、 xy座標と、 TPM_CENTERALIGNフラグ、 TPM_LEFTALIGNフラグ、 TPM_RIGHTALIGNフラグのいずれかを指定します。フラグは、 指定したxy座標に対するメニューの位置を示します。

ポップアップ メニューの項目は、 メニューを表示するときに使ったのと同じマウス ボタンで選択できるようにしてください。TPM_LEFTBUTTONフラグかTPM_RIGHTBUTTONフラグを指定して、 ユーザーがメニュー項目を選択するのに使うマウス ボタンを示してください。

 

フローティング フォント属性メニューの作成

次の例は、 フォントとフォント属性を設定するためのフローティング ポップアップ メニューを作成、 表示する方法を示しています。ユーザーがマウス ボタンをクリックすると、 アプリケーションは、 メイン ウィンドウのクライアント領域にメニューを表示します。

アプリケーションのリソース定義ファイルに指定するポップアップ メニューのメニュー テンプレートを次に示します。

 

PopupMenu MENU
BEGIN
POPUP "Dummy Popup"
BEGIN
POPUP "Fonts"
BEGIN
MENUITEM "Courier", IDM_FONT_COURIER
MENUITEM "Times Roman", IDM_FONT_TMSRMN
MENUITEM "Swiss", IDM_FONT_SWISS
MENUITEM "Helvetica", IDM_FONT_HELV
MENUITEM "Old English", IDM_FONT_OLDENG
END
POPUP "Sizes"
BEGIN
MENUITEM "7", IDM_SIZE_7
MENUITEM "8", IDM_SIZE_8
MENUITEM "9", IDM_SIZE_9
MENUITEM "10", IDM_SIZE_10
MENUITEM "11", IDM_SIZE_11
MENUITEM "12", IDM_SIZE_12
MENUITEM "14", IDM_SIZE_14
END
POPUP "Styles"
BEGIN
MENUITEM "Bold", IDM_STYLE_BOLD
MENUITEM "Italic", IDM_STYLE_ITALIC
MENUITEM "Strike Out", IDM_STYLE_SO
MENUITEM "Superscript", IDM_STYLE_SUPER
MENUITEM "Subscript", IDM_STYLE_SUB
END
END
END

ウィンドウ プロシージャと、 フローティング ポップアップ メニューの作成や表示に使われるサポート関数を次に示します。

 

LRESULT APIENTRY MenuWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
RECT rc; /* client area */
POINT pt; /* location of mouse click */

switch (uMsg) {
case WM_LBUTTONDOWN:

/* Get the bounding rectangle of the client area. */

GetClientRect(hwnd, (LPRECT) &rc);

/* Get the client coordinates for the mouse click. */

pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);

/*
* If the mouse click took place inside the client
* area, execute the application-defined function
* that displays the floating pop-up menu.
*/

if (PtInRect((LPRECT) &rc, pt))
HandlePopupMenu(hwnd, pt);
break;
.
. /* Process other window messages. */
.

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}


VOID APIENTRY HandlePopupMenu(hwnd, pt)
HWND hwnd;
POINT pt;
{
HMENU hmenu; /* menu template */
HMENU hmenuTrackPopup; /* floating pop-up menu */

/*
* Load the menu template containing the pop-up menu from the
* application's resources.
*/

hmenu = LoadMenu(hinst, "PopupMenu");
if (hmenu == NULL)
return;

/*
* Get the first pop-up menu in the menu template. This is the
* menu that TrackPopupMenu displays.
*/

hmenuTrackPopup = GetSubMenu(hmenu, 0);

/*
* TrackPopup uses screen coordinates, so convert the
* coordinates of the mouse click to screen coordinates.
*/

ClientToScreen(hwnd, (LPPOINT) &pt);

/* Draw and track the floating pop-up menu. */

TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON,
pt.x, pt.y, 0, hwnd, NULL);

/* Destroy the menu.*/

DestroyMenu(hmenu);
}

 

 

メニュー項目ビットマップの使用

Windowsでは、 テキスト文字列ではなくビットマップでメニュー項目を表示できます。ビットマップを使うには、 メニュー項目にMF_BITMAPフラグをセットし、 Windowsがメニュー項目に表示するビットマップのハンドルを指定します。ここでは、 MF_BITMAPフラグをセットし、 ビットマップのハンドルを取得する方法について説明します。

 

MF_BITMAPフラグのセット

MF_BITMAPフラグは、 メニュー項目にテキスト文字列ではなくビットマップを使うようにWindowsに指示します。メニュー項目のMF_BITMAPフラグは、 実行時にセットしてください。このフラグは、 リソース定義ファイルではセットできません。

MF_BITMAPフラグをセットするには、 ModifyMenu関数、 InsertMenu関数、 AppendMenu関数のいずれかを使います。テキスト文字列項目のメニュー項目をビットマップ項目に変更するには、 ModifyMenuを使います。メニューに新しいビットマップ項目を追加するには、 MF_BITMAPフラグを指定してInsertMenu関数かAppendMenu関数を使います。

 

ビットマップの作成

メニュー項目にMF_BITMAPフラグをセットするときは、 メニュー項目にWindowsが表示するビットマップのハンドルも指定しなければなりません。ビットマップは、 ビットマップ リソースとして用意するか、 実行時に作成します。

ビットマップ リソースを使うには、 ImageEditなどのリソース エディタを使ってビットマップ リソースを作成し、 リソース コンパイラを使って、 アプリケーションの実行可能ファイルにビットマップを追加します。そして、 実行時に、 LoadBitmap関数を使ってビットマップをロードし、 ハンドルを取得します。

ビットマップを実行時に作成するには、 GDI関数を使います。実行時にGDIを使ってビットマップを作成する方法はいくつかありますが、 通常は、 次に示す方法を使います。

CreateCompatibleDC関数を使って、 アプリケーションのメイン ウィンドウが使っているデバイス コンテキストと互換性のあるデバイス コンテキストを作成します。

CreateCompatibleBitmap関数を使って、 アプリケーションのメイン ウィンドウと互換性のあるビットマップを作成します。

SelectObject関数を使って、 互換デバイス コンテキストでビットマップを選択します。

EllipseLineToなどのGDI描画関数を使って、 ビットマップにイメージを描画します。

ModifyMenu関数やInsertMenu関数、 AppendMenu関数に、 ビットマップのハンドルを渡します。

 

ビットマップについて詳しくは、 ビットマップの概要を参照してください。

 

メニューへの線やグラフの追加

次のコード例は、 メニュー項目ビットマップを持つメニューを作成する方法を示しています。1つ目は、 円グラフ、 折れ線グラフ、 棒グラフの3つのメニュー項目ビットマップを持つ[Chart]メニューです。この例では、 ビットマップをアプリケーションのリソース ファイルからロードし、 CreatePopupMenu関数とAppendMenu関数を使ってメニューとメニュー項目を作成する方法を示しています。

2つ目のポップアップ メニューは[Lines]メニューです。このメニューには、 Windowsの定義済みペンで描画した線スタイルを示すビットマップがあります。線スタイル ビットマップは、 GDI関数を使って実行時に作成します。

アプリケーションのリソース定義ファイルでのビットマップ リソースの定義を次に示します。

 

PIE BITMAP pie.bmp
LINE BITMAP line.bmp
BAR BITMAP bar.bmp

アプリケーションのヘッダー ファイルの関連する部分を次に示します。

 

/* Menu-item identifiers */

#define IDM_SOLID PS_SOLID
#define IDM_DASH PS_DASH
#define IDM_DASHDOT PS_DASHDOT
#define IDM_DASHDOTDOT PS_DASHDOTDOT

#define IDM_PIE 1
#define IDM_LINE 2
#define IDM_BAR 3

/* Line-type flags */

#define SOLID 0
#define DOT 1
#define DASH 2
#define DASHDOT 3
#define DASHDOTDOT 4

/* Count of pens */

#define CPENS 5

/* Chart-type flags */

#define PIE 1
#define LINE 2
#define BAR 3

/* Function prototypes */

LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);
VOID MakeChartMenu(HWND);
VOID MakeLineMenu(HWND, HPEN, HBITMAP);

次の例は、 アプリケーションでメニューとメニュー項目ビットマップを作成する方法を示しています。

 

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

static HPEN hpen[CPENS];
static HBITMAP hbmp[CPENS];
int i;

switch (uMsg) {
case WM_CREATE:

/* Create the Chart and Line menus. */

MakeChartMenu(hwnd);
MakeLineMenu(hwnd, hpen, hbmp);
return 0;

.
. /* Process other window messages. */
.

case WM_DESTROY:

for (i = 0; i < CPENS; i++) {
DeleteObject(hbmp[i]);
DeleteObject(hpen[i]);
}

PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}

VOID MakeChartMenu(hwnd)
HWND hwnd; /* handle of owner window */
{
HBITMAP hbmpPie; /* handle of pie chart bitmap */
HBITMAP hbmpLine; /* handle of line chart bitmap */
HBITMAP hbmpBar; /* handle of bar chart bitmap */
HMENU hmenuMain; /* handle of main menu */
HMENU hmenuChart; /* handle of chart pop-up menu */

/*
* Load the pie, line, and bar chart bitmaps from the
* resource-definition file.
*/

hbmpPie = LoadBitmap(hinst, MAKEINTRESOURCE(PIE));
hbmpLine = LoadBitmap(hinst, MAKEINTRESOURCE(LINE));
hbmpBar = LoadBitmap(hinst, MAKEINTRESOURCE(BAR));

/*
* Create the Chart pop-up menu and add it to the menu bar.
* Append the Pie, Line, and Bar menu items to the Chart
* pop-up menu.
*/

hmenuMain = GetMenu(hwnd);
hmenuChart = CreatePopupMenu();
AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (UINT) hmenuChart,
"Chart");
AppendMenu(hmenuChart, MF_BITMAP, IDM_PIE, (LPCTSTR) hbmpPie);
AppendMenu(hmenuChart, MF_BITMAP, IDM_LINE,
(LPCTSTR) hbmpLine);
AppendMenu(hmenuChart, MF_BITMAP, IDM_BAR, (LPCTSTR) hbmpBar);

return;
}

VOID MakeLineMenu(hwnd, phpen, phbmp)
HWND hwnd;
HPEN *phpen;
HBITMAP *phbmp;
{
HMENU hmenuLines; /* handle of Lines pop-up menu */
HMENU hmenu; /* handle of main menu */
COLORREF crMenuClr; /* menu-item background color */
HBRUSH hbrBackground; /* handle of background brush */
HBRUSH hbrOld; /* handle of previous brush */
LONG lCheckXY; /* dimensions of check mark bitmap */
WORD wLineX; /* width of line bitmaps */
WORD wLineY; /* height of line bitmaps */
HDC hdcMain; /* handle of main window's DC */
HDC hdcLines; /* handle of compatible DC */
HBITMAP hbmpOld; /* handle of previous bitmap */
int i; /* loop counter */

/* Create the Lines pop-up menu. Add it to the menu bar. */

hmenu = GetMenu(hwnd);
hmenuLines = CreatePopupMenu();
AppendMenu(hmenu, MF_STRING | MF_POPUP,
(UINT) hmenuLines, "&Lines");

/* Create a brush for the menu-item background color. */

crMenuClr = GetSysColor(COLOR_MENU);
hbrBackground = CreateSolidBrush(crMenuClr);

/*
* Create a compatible device context for the line bitmaps,
* and then select the background brush into it.
*/

hdcMain = GetDC(hwnd);
hdcLines = CreateCompatibleDC(hdcMain);
hbrOld = SelectObject(hdcLines, hbrBackground);

/*
* Get the dimensions of the check mark bitmap. The width of
* the line bitmaps will be five times the width of the
* check mark bitmap.
*/

lCheckXY = GetMenuCheckMarkDimensions();
wLineX = LOWORD(lCheckXY) * (WORD) 5;
wLineY = HIWORD(lCheckXY);

/*
* Create the bitmaps and select them, one at a time, into the
* compatible device context. Initialize each bitmap by
* filling it with the menu-item background color.
*/

for (i = 0; i < CPENS; i++) {
phbmp[i] = CreateCompatibleBitmap(hdcMain, wLineX, wLineY);
if (i == 0)
hbmpOld = SelectObject(hdcLines, phbmp[i]);
else
SelectObject(hdcLines, phbmp[i]);
ExtFloodFill(hdcLines, 0, 0, crMenuClr, FLOODFILLBORDER);
}

/* Create the pens. */

phpen[0] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
phpen[1] = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
phpen[2] = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
phpen[3] = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0));
phpen[4] = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0));

/*
* Select a pen and a bitmap into the compatible device
* context, draw a line into the bitmap, and then append
* the bitmap as an item in the Lines menu.
*/

for (i = 0; i < CPENS; i++) {
SelectObject(hdcLines, phbmp[i]);
SelectObject(hdcLines, phpen[i]);
MoveToEx(hdcLines, 0, wLineY / 2, NULL);
LineTo(hdcLines, wLineX, wLineY / 2);
AppendMenu(hmenuLines, MF_BITMAP, i + 1,
(LPCTSTR) phbmp[i]);
}

/*
* Release the main window's device context and destroy the
* compatible device context. Also, destroy the background
* brush.
*/

ReleaseDC(hwnd, hdcMain);
SelectObject(hdcLines, hbrOld);
DeleteObject(hbrBackground);
SelectObject(hdcLines, hbmpOld);
DeleteDC(hdcLines);

return;
}

 

 

オーナー描画メニュー項目の作成

メニューの外観を完全に制御したいときは、 オーナー描画メニュー項目を使います。

 

MF_OWNERDRAWフラグのセット

オーナー描画メニュー項目は、 アプリケーションのリソース定義ファイルでは定義できません。オーナー描画メニュー項目は、 MF_OWNERDRAWフラグを使って、 新しいメニュー項目として作成するか、 既存のメニューを修正して作成してください。

オーナー描画メニュー項目を指定するには、 AppendMenu関数やInsertMenu関数、 ModifyMenu関数を使います。メニューの最後に新しい項目を追加するには、 AppendMenuを使います。メニューの途中に新しい項目を置き、 それ以降の項目をずらすには、 InsertMenuを使います。メニューの内容を変更するには、 ModifyMenuを使います。

上記の3つの関数を呼び出すときは、 lpNewItemパラメータに32ビット値を指定できます。この値はアプリケーションに固有な情報を表現するためのもので、 アプリケーションが項目を表示するときに利用できます。たとえば、 テキスト文字列とその文字列を描画するために使う論理フォントのハンドルを示す構造体を指すポインタをこの値に指定し、 項目を描画するときに使うことができます。

 

WM_MEASUREITEMメッセージの処理

Windowsは、 オーナー描画メニュー項目を最初に表示する前に、 項目のメニューを所有するウィンドウのウィンドウ プロシージャにWM_MEASUREITEMメッセージを送ります。このメッセージは、 項目を識別する値と、 アプリケーションで項目に割り当てた項目データを含むMEASUREITEMSTRUCT構造体を指すポインタを含みます。ウィンドウ プロシージャは、 メッセージの処理から戻る前に、 構造体のitemWidthメンバとitemHeightメンバに値を設定しなければなりません。Windowsは、 このメンバの情報を使って、 アプリケーションがメニュー項目を描画する境界長方形を描画します。また、 この情報は、 ユーザーによるメニュー選択を検出するのにも使われます。

 

WM_DRAWITEMメッセージの処理

(メニュー項目を最初に表示するときやユーザーが選択したときなど) 項目を描画しなければならないとき、 Windowsは、 メニューのオーナー ウィンドウのウィンドウ プロシージャにWM_DRAWITEMメッセージを送ります。このメッセージは、 DRAWITEMSTRUCT構造体を指すポインタを含みます。DRAWITEMSTRUCTは、 アプリケーションで割り当てた項目データを含んだ、 項目に関する情報を示します。さらに、 DRAWITEMSTRUCTには、 項目の状態 (灰色表示やチェックなど) を示すフラグや、 アプリケーションが項目を描画するのに使う境界長方形やデバイス コンテキストもあります。

アプリケーションは、 WM_DRAWITEMメッセージを処理している間に、 次に示す動作を実行しなければなりません。

1. 必要な描画の種類を判断します。DRAWITEMSTRUCT構造体のitemActionメンバを調べます。

2. DRAWITEMSTRUCT構造体から取得した境界長方形とDCを使って、 メニュー項目を描画します。アプリケーションは、 境界長方形内だけに描画してください。性能を向上させるため、 Windowsは、 境界長方形の外側に描画されるイメージをクリップしません。

3. メニュー項目のデバイス コンテキストで選択したGDIオブジェクトをすべて復元します。

 

メニュー項目が選択されると、 Windowsは、 DRAWITEMSTRUCT構造体のitemActionメンバにODA_SELECTフラグをセットし、 itemStateメンバにODS_SELECTEDフラグをセットします。アプリケーションは、 この情報を使って、 メニュー項目が選択されていることを示すように再描画できます。

 

メニュー項目テキスト文字列のフォントの設定

ここでは、 ポップアップ メニューでオーナー描画メニュー項目を使う例を示します。ポップアップ メニューには、 現在のフォントの属性を設定する項目があり、 項目は適切なフォント属性で表示されます。

リソース定義ファイルでのメニューの定義を次に示します。[Regular][Bold][Italic][Underline]の各項目は実行時に設定するため、 リソース定義ファイルの文字列は空になっています。

 

MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "", IDM_REGULAR
MENUITEM SEPARATOR
MENUITEM "", IDM_BOLD
MENUITEM "", IDM_ITALIC
MENUITEM "", IDM_ULINE
END
END

アプリケーションのウィンドウ プロシージャは、 オーナー描画メニュー項目に関するメッセージを処理します。アプリケーションは、 WM_CREATEメッセージを使って、 次に示す処理を行います。

・ メニュー項目にMF_OWNERDRAWフラグをセットします。

・ メニュー項目にテキスト文字列を設定します。

・ 項目の描画に使うフォントのハンドルを取得します。

・ 選択されたメニュー項目のテキストと背景の色の値を取得します。

 

テキスト文字列とフォント ハンドルは、 アプリケーション定義のMYITEM構造体の配列に格納します。アプリケーション定義のGetAFont関数は、 指定されたフォント属性に対応するフォントを作成し、 そのフォントのハンドルを返します。このハンドルは、 WM_DESTROYメッセージを処理するときに破棄します。

この例では、 WM_MEASUREITEMメッセージを処理するとき、 メニュー項目文字列の幅と高さを取得し、 その値をMEASUREITEMSTRUCT構造体にコピーします。Windowsは、 幅と高さの値を使って、 ポップアップ メニューのサイズを計算します。

WM_DRAWITEMメッセージを処理するとき、 チェック マーク ビットマップのための余白を空けてメニュー項目の文字列を描画します。ユーザーが項目を選択すると、 テキストの色と背景色を使って項目を描画します。

 

LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

typedef struct _MYITEM {
HFONT hfont;
LPSTR psz;
} MYITEM; /* structure for item font and string */

MYITEM *pmyitem; /* pointer to item's font and string */
static MYITEM myitem[CITEMS]; /* array of MYITEMS */
static HMENU hmenu; /* handle of main menu */
static COLORREF crSelText; /* text color of selected item */
static COLORREF crSelBkgnd; /* background color of selected item */
COLORREF crText; /* text color of unselected item */
COLORREF crBkgnd; /* background color unselected item */
LPMEASUREITEMSTRUCT lpmis; /* points to item of data */
LPDRAWITEMSTRUCT lpdis; /* points to item drawing data */
HDC hdc; /* handle of screen DC */
SIZE size; /* menu-item text extents */
DWORD dwCheckXY; /* check mark dimensions */
WORD wCheckX; /* check mark width */
int nTextX; /* width of menu item */
int nTextY; /* height of menu item */
int i; /* loop counter */
HFONT hfontOld; /* handle of old font */
BOOL fSelected = FALSE; /* menu-item selection flag */

switch (uMsg) {
case WM_CREATE:

/*
* Modify the Regular, Bold, Italic, and Underline
* menu items to make them owner-drawn items. Associate
* a MYITEM structure with each item to contain the
* string and font handle for each item.
*/

hmenu = GetMenu(hwnd);
ModifyMenu(hmenu, IDM_REGULAR, MF_BYCOMMAND |
MF_CHECKED | MF_OWNERDRAW, IDM_REGULAR,
(LPTSTR) &myitem[REGULAR]);
ModifyMenu(hmenu, IDM_BOLD, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_BOLD, (LPTSTR) &myitem[BOLD]);
ModifyMenu(hmenu, IDM_ITALIC, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_ITALIC,
(LPTSTR) &myitem[ITALIC]);
ModifyMenu(hmenu, IDM_ULINE, MF_BYCOMMAND |
MF_OWNERDRAW, IDM_ULINE, (LPTSTR) &myitem[ULINE]);

/*
* Retrieve each item's font handle and copy it into
* the hfont member of each item's MYITEM structure.
* Also, copy each item's string into the structures.
*/

myitem[REGULAR].hfont = GetAFont(REGULAR);
myitem[REGULAR].psz = "Regular";
myitem[BOLD].hfont = GetAFont(BOLD);
myitem[BOLD].psz = "Bold";
myitem[ITALIC].hfont = GetAFont(ITALIC);
myitem[ITALIC].psz = "Italic";
myitem[ULINE].hfont = GetAFont(ULINE);
myitem[ULINE].psz = "Underline";

/*
* Retrieve the text and background colors of the
* selected menu text.
*/

crSelText = GetSysColor(COLOR_HIGHLIGHTTEXT);
crSelBkgnd = GetSysColor(COLOR_HIGHLIGHT);

return 0;

case WM_MEASUREITEM:

/* Retrieve a device context for the main window. */

hdc = GetDC(hwnd);

/*
* Retrieve pointers to the menu item's
* MEASUREITEMSTRUCT structure and MYITEM structure.
*/

lpmis = (LPMEASUREITEMSTRUCT) lParam;
pmyitem = (MYITEM *) lpmis->itemData;

/*
* Select the font associated with the item into
* the main window's device context.
*/

hfontOld = SelectObject(hdc, pmyitem->hfont);

/*
* Retrieve the width and height of the item's string,
* and then copy the width and height into the
* MEASUREITEMSTRUCT structure's itemWidth and
* itemHeight members.
*/

GetTextExtentPoint32(hdc, pmyitem->psz,
lstrlen(pmyitem->psz), &size);
lpmis->itemWidth = size.cx;
lpmis->itemHeight = size.cy;

/*
* Select the old font back into the device context,
* and then release the device context.
*/

SelectObject(hdc, hfontOld);
ReleaseDC(hwnd, hdc);

return TRUE;

break;

case WM_DRAWITEM:

/*
* Get pointers to the menu item's DRAWITEMSTRUCT
* structure and MYITEM structure.
*/

lpdis = (LPDRAWITEMSTRUCT) lParam;
pmyitem = (MYITEM *) lpdis->itemData;

/*
* If the user has selected the item, use the selected
* text and background colors to display the item.
*/

if (lpdis->itemState & ODS_SELECTED) {
crText = SetTextColor(lpdis->hDC, crSelText);
crBkgnd = SetBkColor(lpdis->hDC, crSelBkgnd);
fSelected = TRUE;
}

/*
* Remember to leave space in the menu item for the
* check mark bitmap. Retrieve the width of the bitmap
* and add it to the width of the menu item.
*/

dwCheckXY = GetMenuCheckMarkDimensions();
wCheckX = LOWORD(dwCheckXY);
nTextX = wCheckX + lpdis->rcItem.left;
nTextY = lpdis->rcItem.top;

/*
* Select the font associated with the item into the
* item's device context, and then draw the string.
*/

hfontOld = SelectObject(lpdis->hDC, pmyitem->hfont);
ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE,
&lpdis->rcItem, pmyitem->psz,
lstrlen(pmyitem->psz), NULL);

/*
* Select the previous font back into the device
* context.
*/

SelectObject(lpdis->hDC, hfontOld);

/*
* Return the text and background colors to their
* normal state (not selected).
*/

if (fSelected) {
SetTextColor(lpdis->hDC, crText);
SetBkColor(lpdis->hDC, crBkgnd);
}

return TRUE;

.
. /* Process other messages. */
.


case WM_DESTROY:

/* Destroy the menu items' font handles. */

for (i = 0; i < CITEMS; i++)
DeleteObject(myitem[i].hfont);

PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return NULL;
}

HFONT GetAFont(fnFont)
int fnFont; /* font-attribute flag */
{
static LOGFONT lf; /* structure for font information */

/*
* Get a handle to the ANSI fixed-pitch font, and copy
* information about the font to a LOGFONT structure.
*/

GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT),
&lf);

/* Set the font attributes, as appropriate. */

if (fnFont == BOLD)
lf.lfWeight = FW_BOLD;
else
lf.lfWeight = FW_NORMAL;

lf.lfItalic = (fnFont == ITALIC);
lf.lfItalic = (fnFont == ULINE);

/* Create the font, and then return its handle. */

return CreateFont(lf.lfHeight, lf.lfWidth,
lf.lfEscapement, lf.lfOrientation, lf.lfWeight,
lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet,
lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality,
lf.lfPitchAndFamily, lf.lfFaceName);
}

 

 

カスタムのチェック マーク ビットマップの使用

Windowsは、 チェック状態のメニュー項目に表示されるデフォルトのチェック マーク ビットマップを用意しています。個々のメニュー項目をカスタマイズするには、 デフォルトのチェック マーク ビットマップを置き換えるビットマップのペアを用意します。Windowsは、 項目がチェックされているときとチェックされていないときそれぞれに別のビットマップを表示できます。ここでは、 カスタムのチェック マーク ビットマップの作成と使用に関する手順について説明します。

 

カスタムのチェック マーク ビットマップの作成

カスタムのチェック マーク ビットマップのサイズは、 デフォルトのチェック マーク ビットマップと同じでなければなりません。デフォルトのチェック マーク ビットマップのサイズを取得するには、 GetMenuCheckMarkDimensions関数を呼び出します。この関数の戻り値の下位ワードが幅を示し、 上位ワードが高さを示しています。

チェック マーク ビットマップには、 ビットマップ リソースも使えます。しかし、 ディスプレイの種類によって、 必要なビットマップ サイズが異なるため、 必要に応じてStretchBlt関数を使って、 実行時にビットマップを伸縮しなければなりません。伸縮するビットマップのサイズによっては、 伸縮によってビットマップが歪むことがあるため、 適切なビットマップが得られない場合もあります。

ビットマップ リソースを使わずに、 GDI関数を使って実行時にチェック マーク ビットマップを作成することもできます。この方法を次に示します。

1. CreateCompatibleDC関数を使って、 アプリケーションのメイン ウィンドウで使われているDCと互換性のあるDCを作成します。

2. CreateCompatibleBitmap関数を使って、 アプリケーションのメイン ウィンドウと互換性のあるビットマップを作成します。または、 CreateBitmap関数を使って、 モノクロのビットマップを作成します。

3. SelectObject関数を使って、 ビットマップを互換DCに選択します。

4. EllipseLineToなどのGDI描画関数を使って、 イメージをビットマップに描画します。

 

ビットマップについて詳しくは、 ビットマップの概要を参照してください。

 

ビットマップとメニュー項目の関連付け

メニュー項目にチェック マーク ビットマップのペアを関連付けるには、 ビットマップのハンドルをSetMenuItemBitmaps関数に渡します。hbmUncheckedパラメータには、 非チェック状態のビットマップを指定します。hbmCheckedパラメータには、 チェック状態のビットマップを指定します。メニュー項目からチェック マークの両方または一方を削除するには、 hbmUncheckedパラメータやhbmCheckedパラメータにNULLを指定します。

 

チェック マーク属性の設定

CheckMenuItem関数は、 メニュー項目のチェック マーク属性をチェックまたは非チェックに設定します。チェック マーク属性をチェック状態に設定するには、 MF_CHECKEDフラグを指定します。また、 非チェック状態に設定するには、 MF_UNCHECKEDフラグを指定します。

 

メニューでのチェック ボックスのシミュレート

ここでは、 ポップアップ メニューでチェック ボックスをシミュレートする方法を示します。この例では、 現在のフォントのボールドやイタリック、 下線などの属性をユーザーが設定するための項目を持つ[Character]メニューを使います。フォント属性が有効ならば、 対応するメニュー項目の隣のチェック ボックスにチェック マークを表示します。フォント属性が有効でなければ、 項目の隣に空のチェック ボックスを表示します。

この例では、 デフォルトのチェック マーク ビットマップを2つのビットマップで置き換えています。ビットマップは、 チェック ボックスのビットマップと、 空のボックスのビットマップです。[Bold][Italic][Underline]の各メニュー項目のチェック マーク属性がMF_CHECKEDに設定されると、 チェックされているチェック ボックスのビットマップを項目の隣に表示します。チェック マーク属性がMF_UNCHECKEDに設定されると、 空の (チェックされていない) チェック ボックス ビットマップを表示します。

Windowsは、 チェック ボックスやオプション ボタンに使われるイメージを含む定義済みビットマップを用意しています。この例では、 チェック ボックスと空のボックスを別々に作成して、 独立した2つのビットマップにコピーし、 [Character]メニューのメニュー項目のチェック ビットマップおよび非チェック ビットマップとして使っています。

この例では、 システム定義のチェック ボックス ビット マップのハンドルを取得するため、 hinstパラメータにNULL、 lpBitmapNameパラメータにOBM_CHECKBOXESを指定してLoadBitmap関数を呼び出しています。ビットマップ内のイメージのサイズはすべて同じであるため、 イメージの列数と行数でビットマップの幅と高さを割れば、 イメージを分割できます。

次に示すリソース定義ファイルの一部は、 [Character]メニューのメニュー項目の定義を示しています。最初はどのフォント属性も有効ではないため、 [Regular]項目のチェック マーク属性をチェック状態に設定しています。また、 ほかの項目のチェック マーク属性はデフォルトで非チェック状態になっています。

 

#include "men3.h"

MainMenu MENU
BEGIN
POPUP "&Character"
BEGIN
MENUITEM "&Regular", IDM_REGULAR, CHECKED
MENUITEM SEPARATOR
MENUITEM "&Bold", IDM_BOLD
MENUITEM "&Italic", IDM_ITALIC
MENUITEM "&Underline", IDM_ULINE
END
END

アプリケーションのヘッダー ファイルの関連する部分を次に示します。

 

/* Menu-item identifiers */

#define IDM_REGULAR 0x1
#define IDM_BOLD 0x2
#define IDM_ITALIC 0x4
#define IDM_ULINE 0x8

/* Check mark flags */

#define CHECK 1
#define UNCHECK 2

/* Font-attribute mask */

#define ATTRIBMASK 0xe

/* Function prototypes */

LRESULT APIENTRY MainWndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP GetMyCheckBitmaps(UINT);
BYTE CheckOrUncheckMenuItem(BYTE, HMENU);

次の例は、 ウィンドウ プロシージャの一部を示しています。このコードは、 チェック マーク ビットマップの作成、 [Bold][Italic][Underline]の各メニュー項目のチェック マーク属性の設定、 チェック マーク ビットマップの破棄を行います。

 

LRESULT APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;

UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{

static HBITMAP hbmpCheck; /* handle of checked bitmap */
static HBITMAP hbmpUncheck; /* handle of unchecked bitmap */
static HMENU hmenu; /* handle of main menu */
BYTE fbFontAttrib; /* font-attribute flags */

switch (uMsg) {
case WM_CREATE:

/*
* Call the application-defined GetMyCheckBitmaps
* function to get the predefined checked and
* unchecked check box bitmaps.
*/

hbmpCheck = GetMyCheckBitmaps(CHECK);
hbmpUncheck = GetMyCheckBitmaps(UNCHECK);

/*
* Set the checked and unchecked bitmaps for the menu
* items.
*/

hmenu = GetMenu(hwndMain);
SetMenuItemBitmaps(hmenu, IDM_BOLD, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
SetMenuItemBitmaps(hmenu, IDM_ITALIC, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);
SetMenuItemBitmaps(hmenu, IDM_ULINE, MF_BYCOMMAND,
hbmpUncheck, hbmpCheck);

return 0;

case WM_COMMAND:
switch (LOWORD(wParam)) {

/* Process the menu commands. */

case IDM_REGULAR:
case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:

/*
* CheckOrUncheckMenuItem is an application-
* defined function that sets the menu item
* check marks and returns the user-selected
* font attributes.
*/

fbFontAttrib = CheckOrUncheckMenuItem(
(BYTE) LOWORD(wParam), hmenu);

.
. /* Set the font attributes. */
.

return 0;

.
. /* Process other command messages. */
.

default:
break;
}

break;

.
. /* Process other window messages. */
.

case WM_DESTROY:

/* Destroy the checked and unchecked bitmaps. */

DeleteObject(hbmpCheck);
DeleteObject(hbmpUncheck);

PostQuitMessage(0);
break;

default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}

HBITMAP GetMyCheckBitmaps(fuCheck)
UINT fuCheck; /* CHECK or UNCHECK flag */
{
COLORREF crBackground; /* background color */
HBRUSH hbrBackground; /* background brush */
HBRUSH hbrTargetOld; /* original background brush */
HDC hdcSource; /* source device context */
HDC hdcTarget; /* target device context */
HBITMAP hbmpCheckboxes; /* handle of check box bitmap */
BITMAP bmCheckbox; /* structure for bitmap data */
HBITMAP hbmpSourceOld; /* handle of original source bitmap */
HBITMAP hbmpTargetOld; /* handle of original target bitmap */
HBITMAP hbmpCheck; /* handle of check mark bitmap */
RECT rc; /* rectangle for check box bitmap */
DWORD dwCheckXY; /* dimensions of check mark bitmap */
WORD wBitmapX; /* width of check mark bitmap */
WORD wBitmapY; /* height of check mark bitmap */

/*
* Get the menu background color and create a solid brush
* with that color.
*/

crBackground = GetSysColor(COLOR_MENU);
hbrBackground = CreateSolidBrush(crBackground);

/*
* Create memory device contexts for the source and
* destination bitmaps.
*/

hdcSource = CreateCompatibleDC((HDC) NULL);
hdcTarget = CreateCompatibleDC(hdcSource);

/*
* Get the size of the Windows default check mark bitmap and
* create a compatible bitmap of the same size.
*/

dwCheckXY = GetMenuCheckMarkDimensions();
wBitmapX = LOWORD(dwCheckXY);
wBitmapY = LOWORD(dwCheckXY);

hbmpCheck = CreateCompatibleBitmap(hdcSource, wBitmapX,
wBitmapY);

/*
* Select the background brush and bitmap into the target DC.
*/

hbrTargetOld = SelectObject(hdcTarget, hbrBackground);
hbmpTargetOld = SelectObject(hdcTarget, hbmpCheck);

/*
* Use the selected brush to initialize the background color
* of the bitmap in the target device context.
*/

PatBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY, PATCOPY);

/*
* Load the predefined check box bitmaps and select it
* into the source DC.
*/

hbmpCheckboxes = LoadBitmap((HINSTANCE) NULL,
(LPTSTR) OBM_CHECKBOXES);

hbmpSourceOld = SelectObject(hdcSource, hbmpCheckboxes);

/*
* Fill a BITMAP structure with information about the
* check box bitmaps, and then find the upper-left corner of
* the unchecked check box or the checked check box.
*/

GetObject(hbmpCheckboxes, sizeof(BITMAP), &bmCheckbox);

if (fuCheck == UNCHECK) {
rc.left = 0;
rc.right = (bmCheckbox.bmWidth / 4);
}
else {
rc.left = (bmCheckbox.bmWidth / 4);
rc.right = (bmCheckbox.bmWidth / 4) * 2;
}

rc.top = 0;
rc.bottom = (bmCheckbox.bmHeight / 3);

/*
* Copy the appropriate bitmap into the target DC. If the
* check box bitmap is larger than the default check mark
* bitmap, use StretchBlt to make it fit; otherwise, just
* copy it.
*/

if (((rc.right - rc.left) > (int) wBitmapX) ||
((rc.bottom - rc.top) > (int) wBitmapY))
StretchBlt(hdcTarget, 0, 0, wBitmapX, wBitmapY,
hdcSource, rc.left, rc.top, rc.right - rc.left,
rc.bottom - rc.top, SRCCOPY);

else
BitBlt(hdcTarget, 0, 0, rc.right - rc.left,
rc.bottom - rc.top,
hdcSource, rc.left, rc.top, SRCCOPY);

/*
* Select the old source and destination bitmaps into the
* source and destination DCs, and then delete the DCs and
* the background brush.
*/

SelectObject(hdcSource, hbmpSourceOld);
SelectObject(hdcTarget, hbrTargetOld);
hbmpCheck = SelectObject(hdcTarget, hbmpTargetOld);

DeleteObject(hbrBackground);
DeleteObject(hdcSource);
DeleteObject(hdcTarget);

/* Return the handle of the new check mark bitmap. */

return hbmpCheck;
}


BYTE CheckOrUncheckMenuItem(bMenuItemID, hmenu)
BYTE bMenuItemID;
HMENU hmenu;
{
DWORD fdwMenu;
static BYTE fbAttributes;

switch (bMenuItemID) {
case IDM_REGULAR:

/*
* Whenever the Regular menu item is selected, add a
* check mark to it and then remove check marks from
* any font-attribute menu items.
*/

CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND |
MF_CHECKED);

if (fbAttributes & ATTRIBMASK) {
CheckMenuItem(hmenu, IDM_BOLD, MF_BYCOMMAND |
MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_ITALIC, MF_BYCOMMAND |
MF_UNCHECKED);
CheckMenuItem(hmenu, IDM_ULINE, MF_BYCOMMAND |
MF_UNCHECKED);
}
fbAttributes = IDM_REGULAR;
return fbAttributes;

case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:

/*
* Toggle the check mark for the selected menu item and
* set the font attribute flags appropriately.
*/

fdwMenu = GetMenuState(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND);
if (!(fdwMenu & MF_CHECKED)) {
CheckMenuItem(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND | MF_CHECKED);
fbAttributes |= bMenuItemID;

} else {
CheckMenuItem(hmenu, (UINT) bMenuItemID,
MF_BYCOMMAND | MF_UNCHECKED);
fbAttributes ^= bMenuItemID;
}

/*
* If any font attributes are currently selected,
* remove the check mark from the Regular menu item;
* if no attributes are selected, add a check mark
* to the Regular menu item.
*/

if (fbAttributes & ATTRIBMASK) {
CheckMenuItem(hmenu, IDM_REGULAR,
MF_BYCOMMAND | MF_UNCHECKED);
fbAttributes &= (BYTE) ~IDM_REGULAR;

} else {
CheckMenuItem(hmenu, IDM_REGULAR,
MF_BYCOMMAND | MF_CHECKED);
fbAttributes = IDM_REGULAR;
}

return fbAttributes;
}
}

 

 

メニュー関数とメッセージ

 

関数

AppendMenu

CheckMenuItem

CreateMenu

CreatePopupMenu

DeleteMenu

DestroyMenu

DrawMenuBar

EnableMenuItem

GetMenu

GetMenuCheckMarkDimensions

GetMenuItemCount

GetMenuItemID

GetMenuState

GetMenuString

GetSubMenu

GetSystemMenu

HiliteMenuItem

InsertMenu

IsMenu

LoadMenu

LoadMenuIndirect

ModifyMenu

RemoveMenu

SetMenu

SetMenuItemBitmaps

TrackPopupMenu

 

使われなくなったか、 または削除された関数

ChangeMenu

 

メッセージ

WM_DRAWITEM

WM_ENTERMENULOOP

WM_EXITMENULOOP

WM_INITMENU

WM_INITMENUPOPUP

WM_MEASUREITEM

WM_MENUCHAR

WM_MENUSELECT

WM_SYSCOMMAND

 

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします
inserted by FC2 system