メニューの使用

このセクションでは、次のタスクについて説明します。

Menu-Template リソースの使用

通常、メニュー テンプレート リソースを作成し、実行時にメニューを読み込むことで、アプリケーションにメニューを含めます。 このセクションでは、メニュー テンプレートの形式について説明し、メニュー テンプレート リソースを読み込んでアプリケーションで使用する方法について説明します。 メニュー テンプレート リソースの作成の詳細については、開発ツールに含まれているドキュメントを参照してください。

拡張Menu-Template形式

拡張メニュー テンプレート形式では、追加のメニュー機能がサポートされています。 標準のメニュー テンプレート リソースと同様に、拡張メニュー テンプレート リソースには RT_MENU リソースの種類があります。 2 つのリソース形式は、リソース ヘッダーの最初のメンバーであるバージョン番号によって区別されます。

拡張メニュー テンプレートは、 MENUEX_TEMPLATE_HEADER 構造の後に、項目定義構造体 MENUEX_TEMPLATE_ITEM 1 つ続いて構成されます。

古いMenu-Template形式

古いメニュー テンプレート (Microsoft Windows NT 3.51 以前) ではメニューが定義されていますが、新しいメニュー機能はサポートされていません。 古いメニュー テンプレート リソースには、 RT_MENU リソースの種類があります。

古いメニュー テンプレートは、 MENUITEMTEMPLATEHEADER 構造体とそれに続く 1 つ以上 の MENUITEMTEMPLATE 構造体で構成されます。

Menu-Template リソースの読み込み

メニュー テンプレート リソースを読み込むには、 LoadMenu 関数を使用して、リソースとメニュー テンプレートの識別子を含むモジュールへのハンドルを指定します。 LoadMenu 関数は、ウィンドウにメニューを割り当てるために使用できるメニュー ハンドルを返します。 このウィンドウはメニューの所有者ウィンドウになり、メニューによって生成されたすべてのメッセージを受信します。

既にメモリ内にあるメニュー テンプレートからメニューを作成するには、 LoadMenuIndirect 関数を使用します。 これは、アプリケーションがメニュー テンプレートを動的に生成する場合に便利です。

ウィンドウにメニューを割り当てるには、SetMenu 関数を使用するか、ウィンドウの作成時に CreateWindowEx 関数の hMenu パラメーターでメニューのハンドルを指定します。 ウィンドウにメニューを割り当てることができるもう 1 つの方法は、ウィンドウ クラスを登録するときにメニュー テンプレートを指定することです。テンプレートは、指定されたメニューをそのウィンドウ クラスのクラス メニューとして識別します。

特定のメニューをウィンドウに自動的に割り当てるには、ウィンドウのクラスを登録するときにメニューのテンプレートを指定します。 テンプレートは、指定されたメニューをそのウィンドウ クラスのクラス メニューとして識別します。 次に、指定したクラスのウィンドウを作成すると、指定したメニューが自動的にウィンドウに割り当てられます。

子ウィンドウであるウィンドウにメニューを割り当てることはできません。

クラス メニューを作成するには、メニュー テンプレート リソースの識別子を WNDCLASS 構造体の lpszMenuName メンバーとして含め、構造体へのポインターを RegisterClass 関数に渡します。

クラス メニューの作成

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

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

// Menu-template resource identifier 
 
#define IDM_MYMENURESOURCE   3

アプリケーション自体の関連部分を次に示します。

HINSTANCE hinst; 
 
int APIENTRY WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{ 
    MSG msg = { };  // message 
    WNDCLASS wc;    // windowclass data 
    HWND hwnd;      // handle to the 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 =  MAKEINTRESOURCE(IDM_MYMENURESOURCE); 
    wc.lpszClassName = "MainWClass"; 
 
    if (!RegisterClass(&wc)) 
        return FALSE; 
 
    hinst = hinstance; 
 
    // Create the main window. Set the hmenu parameter to NULL so 
    // that the system 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 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; 
} 

ショートカット メニューの作成

アプリケーションでショートカット メニューを使用するには、そのハンドルを TrackPopupMenuEx 関数に渡します。 アプリケーションは通常、ユーザーが生成したメッセージ (WM_LBUTTONDOWNWM_KEYDOWNなど) に応答して、ウィンドウ プロシージャで TrackPopupMenuEx を呼び出します。

TrackPopupMenuEx では、ポップアップ メニュー ハンドルに加えて、所有者ウィンドウへのハンドル、ショートカット メニューの位置 (画面座標)、およびユーザーが項目の選択に使用できるマウス ボタンを指定する必要があります。

古い TrackPopupMenu 関数は引き続きサポートされていますが、新しいアプリケーションでは TrackPopupMenuEx 関数を使用する必要があります。 TrackPopupMenuEx 関数には TrackPopupMenu と同じパラメーターが必要ですが、画面の一部を指定してメニューを隠す必要はありません。 アプリケーションは、通常、 WM_CONTEXTMENU メッセージを処理するときに、ウィンドウ プロシージャでこれらの関数を呼び出します。

TPM_CENTERALIGN、TPM_LEFTALIGN、または TPM_RIGHTALIGN フラグと共に x 座標と y 座標を指定することで、ショートカット メニューの位置を指定できます。 フラグは、x 座標と y 座標を基準としたショートカット メニューの位置を指定します。

メニューの表示に使用するのと同じマウス ボタンを使用して、ショートカット メニューから項目を選択することをユーザーに許可する必要があります。 これを行うには、 TPM_LEFTBUTTON または TPM_RIGHTBUTTON フラグを指定して、ユーザーがメニュー項目の選択に使用できるマウス ボタンを指定します。

WM_CONTEXTMENU メッセージの処理

WM_CONTEXTMENU メッセージは、アプリケーションのウィンドウ プロシージャが WM_RBUTTONUP または WM_NCRBUTTONUP メッセージを DefWindowProc 関数に渡したときに生成されます。 アプリケーションは、このメッセージを処理して、画面の特定の部分に適したショートカット メニューを表示できます。 アプリケーションにショートカット メニューが表示されない場合は、既定の処理のためにメッセージを DefWindowProc に渡す必要があります。

アプリケーションのウィンドウ プロシージャ に表示 される可能性があるメッセージ処理WM_CONTEXTMENU例を次に示します。 lParam パラメーターの下位ワードと上位ワードは、マウスの右ボタンが離されたときのマウスの画面座標を指定します (これらの座標は、複数のモニターを持つシステムで負の値を取る可能性があることに注意してください)。 アプリケーション定義 の OnContextMenu 関数は、コンテキスト メニューを表示する場合は TRUE を返し、表示しない場合は FALSE を 返します。

case WM_CONTEXTMENU: 
    if (!OnContextMenu(hwnd, GET_X_LPARAM(lParam),
              GET_Y_LPARAM(lParam))) 
        return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    break; 

次のアプリケーション定義の OnContextMenu 関数は、指定したマウス位置がウィンドウのクライアント領域内にある場合にショートカット メニューを表示します。 より高度な関数では、クライアント領域のどの部分が指定されているかに応じて、いくつかの異なるメニューのいずれかが表示される場合があります。 この例では、実際にショートカット メニューを表示するために、DisplayContextMenu というアプリケーション定義関数を呼び出します。 この関数の説明については、「 ショートカット メニューの表示」を参照してください。

BOOL WINAPI OnContextMenu(HWND hwnd, int x, int y) 
{ 
    RECT rc;                    // client area of window 
    POINT pt = { x, y };        // location of mouse click 
 
    // Get the bounding rectangle of the client area. 
 
    GetClientRect(hwnd, &rc); 
 
    // Convert the mouse position to client coordinates. 
 
    ScreenToClient(hwnd, &pt); 
 
    // If the position is in the client area, display a  
    // shortcut menu. 
 
    if (PtInRect(&rc, pt)) 
    { 
        ClientToScreen(hwnd, &pt); 
        DisplayContextMenu(hwnd, pt); 
        return TRUE; 
    } 
 
    // Return FALSE if no menu is displayed. 
 
    return FALSE; 
} 

ショートカット Font-Attributes メニューの作成

このセクションの例には、ユーザーがフォントとフォント属性を設定できるようにするショートカット メニューを作成して表示するアプリケーションのコードの一部が含まれています。 アプリケーションは、ユーザーがマウスの左ボタンをクリックするたびに、メイン ウィンドウのクライアント領域にメニューを表示します。

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

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 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 = GET_X_LPARAM(lParam); 
            pt.y = GET_Y_LPARAM(lParam); 
 
            // If the mouse click took place inside the client 
            // area, execute the application-defined function 
            // that displays the shortcut 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 hwnd, POINT pt) 
{ 
    HMENU hmenu;            // menu template          
    HMENU hmenuTrackPopup;  // shortcut menu   
 
    //  Load the menu template containing the shortcut menu from the 
    //  application's resources. 
 
    hmenu = LoadMenu(hinst, "PopupMenu"); 
    if (hmenu == NULL) 
        return; 
 
    // Get the first shortcut 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 shortcut menu.  
 
    TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_LEFTBUTTON, 
        pt.x, pt.y, 0, hwnd, NULL); 
 
    // Destroy the menu. 
 
    DestroyMenu(hmenu); 
} 

ショートカット メニューの表示

次の例に示す関数は、ショートカット メニューを表示します。

アプリケーションには、"ShortcutExample" という文字列で識別されるメニュー リソースが含まれています。メニュー バーには、単にメニュー名が含まれています。 アプリケーションは TrackPopupMenu 関数を使用して、このメニュー項目に関連付けられているメニューを表示します。 ( TrackPopupMenu にはメニュー、サブメニュー、またはショートカット メニューへのハンドルが必要なため、メニュー バー自体は表示されません)。

VOID APIENTRY DisplayContextMenu(HWND hwnd, POINT pt) 
{ 
    HMENU hmenu;            // top-level menu 
    HMENU hmenuTrackPopup;  // shortcut menu 
 
    // Load the menu resource. 
 
    if ((hmenu = LoadMenu(hinst, "ShortcutExample")) == NULL) 
        return; 
 
    // TrackPopupMenu cannot display the menu bar so get 
    // a handle to the first shortcut menu. 
 
    hmenuTrackPopup = GetSubMenu(hmenu, 0); 
 
    // Display the shortcut menu. Track the right mouse 
    // button. 
 
    TrackPopupMenu(hmenuTrackPopup, 
            TPM_LEFTALIGN | TPM_RIGHTBUTTON, 
            pt.x, pt.y, 0, hwnd, NULL); 
 
    // Destroy the menu. 
 
    DestroyMenu(hmenu); 
} 

Menu-Item ビットマップの使用

システムは、テキスト文字列の代わりにビットマップを使用してメニュー項目を表示できます。 ビットマップを使用するには、メニュー項目のMIIM_BITMAP フラグを設定し、MENUITEMINFO 構造体の hbmpItem メンバーのメニュー項目に対してシステムが表示するビットマップへのハンドルを指定する必要があります。 このセクションでは、メニュー項目にビットマップを使用する方法について説明します。

ビットマップ型フラグの設定

MIIM_BITMAP または MF_BITMAP フラグは、テキスト文字列ではなくビットマップを使用してメニュー項目を表示するようにシステムに指示します。 メニュー項目の MIIM_BITMAP または MF_BITMAP フラグは実行時に設定する必要があります。リソース定義ファイルで設定することはできません。

新しいアプリケーションでは、 SetMenuItemInfo または InsertMenuItem 関数を使用して 、MIIM_BITMAP 型フラグを設定できます。 メニュー項目をテキスト項目からビットマップ項目に変更するには、 SetMenuItemInfo を使用します。 メニューに新しいビットマップ項目を追加するには、 InsertMenuItem 関数を使用します。

以前のバージョンのシステム用に作成されたアプリケーションでは、ModifyMenu、InsertMenu、または AppendMenu 関数を引き続き使用して、MF_BITMAP フラグを設定できます。 メニュー項目をテキスト文字列項目からビットマップ項目に変更するには、 ModifyMenu を使用します。 新しいビットマップ項目をメニューに追加するには、InsertMenu または AppendMenu 関数で MF_BITMAP フラグを使用します。

ビットマップの作成

メニュー項目の MIIM_BITMAP または MF_BITMAP 型フラグを設定する場合は、システムがメニュー項目に表示するビットマップのハンドルも指定する必要があります。 ビットマップをビットマップ リソースとして指定することも、実行時にビットマップを作成することもできます。 ビットマップ リソースを使用する場合は、 LoadBitmap 関数を使用してビットマップを読み込み、そのハンドルを取得できます。

実行時にビットマップを作成するには、Windows グラフィックス デバイス インターフェイス (GDI) 関数を使用します。 GDI には、実行時にビットマップを作成するいくつかの方法が用意されていますが、開発者は通常、次のメソッドを使用します。

  1. CreateCompatibleDC 関数を使用して、アプリケーションのメイン ウィンドウで使用されるデバイス コンテキストと互換性のあるデバイス コンテキストを作成します。
  2. CreateCompatibleBitmap 関数を使用して、アプリケーションのメイン ウィンドウと互換性のあるビットマップを作成するか、CreateBitmap 関数を使用してモノクロ ビットマップを作成します。
  3. SelectObject 関数を使用して、互換性のあるデバイス コンテキストにビットマップを選択します。
  4. EllipseLineTo などの GDI 描画関数を使用して、ビットマップにイメージを描画します。

詳細については、「 ビットマップ」を参照してください。

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

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

2 番目のメニューは [線] メニューです。 システム内の定義済みのペンによって提供される線のスタイルを示すビットマップが含まれています。 行スタイルのビットマップは、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 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) 
{ 
    HBITMAP hbmpPie;    // handle to pie chart bitmap   
    HBITMAP hbmpLine;   // handle to line chart bitmap  
    HBITMAP hbmpBar;    // handle to bar chart bitmap   
    HMENU hmenuMain;    // handle to main menu          
    HMENU hmenuChart;   // handle to Chart 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 menu and add it to the menu bar. 
    // Append the Pie, Line, and Bar menu items to the Chart 
    // 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 hwnd, HPEN phpen, HBITMAP phbmp) 
{ 
    HMENU hmenuLines;       // handle to Lines menu      
    HMENU hmenu;            // handle to main menu              
    COLORREF crMenuClr;     // menu-item background color       
    HBRUSH hbrBackground;   // handle to background brush       
    HBRUSH hbrOld;          // handle to previous brush         
    WORD wLineX;            // width of line bitmaps            
    WORD wLineY;            // height of line bitmaps           
    HDC hdcMain;            // handle to main window's DC       
    HDC hdcLines;           // handle to compatible DC          
    HBITMAP hbmpOld;        // handle to previous bitmap        
    int i;                  // loop counter                     
 
    // Create the Lines 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. 
 
    wLineX = GetSystemMetrics(SM_CXMENUCHECK) * (WORD) 5; 
    wLineY = GetSystemMetrics(SM_CYMENUCHECK); 
 
    // 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; 
} 

Menu-Item ビットマップの例

このトピックの例では、それぞれ複数のビットマップ メニュー項目を含む 2 つのメニューを作成します。 アプリケーションは、メニューごとに、対応するメニュー名をメイン ウィンドウのメニュー バーに追加します。

最初のメニューには、円グラフ、折れ線、横棒の 3 種類のグラフのそれぞれを示すメニュー項目が含まれています。 これらのメニュー項目のビットマップはリソースとして定義され、 LoadBitmap 関数を使用して読み込まれます。 このメニューに関連付けられているのは、メニュー バーの [グラフ] メニュー名です。

2 番目のメニューには、CreatePen 関数で使用される 5 つの線スタイル (PS_SOLID、PS_DASH、PS_DOTPS_DASHDOTPS_DASHDOTDOT) を示すメニュー項目が含まれています。 アプリケーションは、GDI 描画関数を使用して、実行時にこれらのメニュー項目のビットマップを作成します。 このメニューに関連付けられているのは、メニュー バーの [線 ] メニュー名です。

アプリケーションのウィンドウ プロシージャで定義されているのは、ビットマップ ハンドルの 2 つの静的配列です。 1 つの配列には、[ グラフ ] メニューに使用される 3 つのビットマップのハンドルが含まれています。 もう 1 つには、[ ] メニューに使用される 5 つのビットマップのハンドルが含まれています。 WM_CREATE メッセージを処理するときに、ウィンドウ プロシージャはグラフビットマップを読み込み、線ビットマップを作成してから、対応するメニュー項目を追加します。 WM_DESTROY メッセージを処理すると、ウィンドウ プロシージャはすべてのビットマップを削除します。

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

// Menu-item identifiers 
 
#define IDM_PIE         1 
#define IDM_LINE        2 
#define IDM_BAR         3 
 
#define IDM_SOLID       4 
#define IDM_DASH        5 
#define IDM_DASHDOT     6 
#define IDM_DASHDOTDOT  7 
 
// Number of items on the Chart and Lines menus 
 
#define C_LINES         5 
#define C_CHARTS        3 
 
// Bitmap resource identifiers 
 
#define IDB_PIE         1 
#define IDB_LINE        2 
#define IDB_BAR         3 
 
// Dimensions of the line bitmaps 
 
#define CX_LINEBMP      40 
#define CY_LINEBMP      10 

ウィンドウ プロシージャの関連部分を次に示します。 ウィンドウ プロシージャは、このトピックで後述するアプリケーション定義の LoadChartBitmaps、CreateLineBitmaps、AddBitmapMenu 関数を呼び出すことによって、初期化の大部分を実行します。

LRESULT CALLBACK MainWindowProc( 
        HWND hwnd, 
        UINT uMsg, 
        WPARAM wParam, 
        LPARAM lParam 
        ) 
{ 
    static HBITMAP aHbmLines[C_LINES]; 
    static HBITMAP aHbmChart[C_CHARTS]; 
    int i; 
 
    switch (uMsg) 
    { 
        case WM_CREATE: 
 
             // Call application-defined functions to load the 
             // bitmaps for the Chart menu and create those for 
             // the Lines menu. 
 
            LoadChartBitmaps(aHbmChart); 
            CreateLineBitmaps(aHbmLines); 
 
             // Call an application-defined function to create 
             // menus containing the bitmap menu items. The function 
             // also adds a menu name to the window's menu bar. 
 
            AddBitmapMenu( 
                    hwnd,      // menu bar's owner window 
                    "&Chart",  // text of menu name on menu bar 
                    IDM_PIE,   // ID of first item on menu 
                    aHbmChart, // array of bitmap handles 
                    C_CHARTS   // number of items on menu 
                    ); 
            AddBitmapMenu(hwnd, "&Lines", IDM_SOLID, 
                    aHbmLines, C_LINES); 
            break; 
 
        case WM_DESTROY: 
            for (i = 0; i < C_LINES; i++) 
                DeleteObject(aHbmLines[i]); 
            for (i = 0; i < C_CHARTS; i++) 
                DeleteObject(aHbmChart[i]); 
            PostQuitMessage(0); 
            break; 
 
        // Process additional messages here. 
 
        default: 
            return (DefWindowProc(hwnd, uMsg, wParam, lParam)); 
    } 
    return 0; 
} 

アプリケーション定義の LoadChartBitmaps 関数は、次のように LoadBitmap 関数を呼び出して、グラフ メニューのビットマップ リソースを読み込みます。

VOID WINAPI LoadChartBitmaps(HBITMAP *paHbm) 
{ 
    paHbm[0] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_PIE)); 
    paHbm[1] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_LINE)); 
    paHbm[2] = LoadBitmap(g_hinst, MAKEINTRESOURCE(IDB_BAR)); 
} 

アプリケーション定義の CreateLineBitmaps 関数は、GDI 描画関数を使用して [線] メニューのビットマップを作成します。 関数は、デスクトップ ウィンドウの DC と同じプロパティを持つメモリ デバイス コンテキスト (DC) を作成します。 関数は、線のスタイルごとにビットマップを作成し、それをメモリ DC に選択して描画します。

VOID WINAPI CreateLineBitmaps(HBITMAP *paHbm) 
{ 
    HWND hwndDesktop = GetDesktopWindow(); 
    HDC hdcDesktop = GetDC(hwndDesktop); 
    HDC hdcMem = CreateCompatibleDC(hdcDesktop); 
    COLORREF clrMenu = GetSysColor(COLOR_MENU); 
    HBRUSH hbrOld; 
    HPEN hpenOld; 
    HBITMAP hbmOld; 
    int fnDrawMode; 
    int i; 
 
     // Create a brush using the menu background color, 
     // and select it into the memory DC. 
 
    hbrOld = SelectObject(hdcMem, CreateSolidBrush(clrMenu)); 
 
     // Create the bitmaps. Select each one into the memory 
     // DC that was created and draw in it. 
 
    for (i = 0; i < C_LINES; i++) 
    { 
        // Create the bitmap and select it into the DC. 
 
        paHbm[i] = CreateCompatibleBitmap(hdcDesktop, 
                CX_LINEBMP, CY_LINEBMP); 
        hbmOld = SelectObject(hdcMem, paHbm[i]); 
 
        // Fill the background using the brush. 
 
        PatBlt(hdcMem, 0, 0, CX_LINEBMP, CY_LINEBMP, PATCOPY); 
 
        // Create the pen and select it into the DC. 
 
        hpenOld = SelectObject(hdcMem, 
                CreatePen(PS_SOLID + i, 1, RGB(0, 0, 0))); 
 
         // Draw the line. To preserve the background color where 
         // the pen is white, use the R2_MASKPEN drawing mode. 
 
        fnDrawMode = SetROP2(hdcMem, R2_MASKPEN); 
        MoveToEx(hdcMem, 0, CY_LINEBMP / 2, NULL); 
        LineTo(hdcMem, CX_LINEBMP, CY_LINEBMP / 2); 
        SetROP2(hdcMem, fnDrawMode); 
 
        // Delete the pen, and select the old pen and bitmap. 
 
        DeleteObject(SelectObject(hdcMem, hpenOld)); 
        SelectObject(hdcMem, hbmOld); 
    } 
 
    // Delete the brush and select the original brush. 
 
    DeleteObject(SelectObject(hdcMem, hbrOld)); 
 
    // Delete the memory DC and release the desktop DC. 
 
    DeleteDC(hdcMem); 
    ReleaseDC(hwndDesktop, hdcDesktop); 
} 

アプリケーション定義の AddBitmapMenu 関数は、メニューを作成し、指定した数のビットマップ メニュー項目を追加します。 次に、指定したウィンドウのメニュー バーに対応するメニュー名を追加します。

VOID WINAPI AddBitmapMenu( 
        HWND hwnd,          // window that owned the menu bar 
        LPSTR lpszText,     // text of menu name on menu bar 
        UINT uID,           // ID of first bitmap menu item 
        HBITMAP *paHbm,     // bitmaps for the menu items 
        int cItems)         // number bitmap menu items 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup = CreatePopupMenu(); 
    MENUITEMINFO mii; 
    int i; 
 
    // Add the bitmap menu items to the menu. 
 
    for (i = 0; i < cItems; i++) 
    { 
        mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_DATA; 
        mii.wID = uID + i; 
        mii.hbmpItem = &paHbm[i]; 
        InsertMenuItem(hmenuPopup, i, TRUE, &mii); 
    } 
 
    // Add a menu name to the menu bar. 
 
    mii.fMask = MIIM_STRING | MIIM_DATA | MIIM_SUBMENU; 
    mii.fType = MFT_STRING; 
    mii.hSubMenu = hmenuPopup; 
    mii.dwTypeData = lpszText; 
    InsertMenuItem(hmenuBar, 
        GetMenuItemCount(hmenuBar), TRUE, &mii); 
} 

Owner-Drawnメニュー項目の作成

メニュー項目の外観を完全に制御する必要がある場合は、アプリケーションで所有者が描画したメニュー項目を使用できます。 このセクションでは、所有者が描画したメニュー項目の作成と使用に関連する手順について説明します。

Owner-Drawn フラグの設定

アプリケーションのリソース定義ファイルで、所有者描画メニュー項目を定義することはできません。 代わりに、新しいメニュー項目を作成するか、 MFT_OWNERDRAW メニュー フラグを使用して既存のメニュー項目を変更する必要があります。

InsertMenuItem または SetMenuItemInfo 関数を使用して、所有者が描画したメニュー項目を指定できます。 InsertMenuItem を使用して、メニュー バーまたはメニュー内の指定した位置に新しいメニュー項目を挿入します。 メニューの内容を変更するには、 SetMenuItemInfo を使用します。

これら 2 つの関数を呼び出すときは、 MENUITEMINFO 構造体へのポインターを指定する必要があります。この構造体は、新しいメニュー項目のプロパティ、または既存のメニュー項目に対して変更するプロパティを指定します。 アイテムを所有者描画アイテムにするには、fMask メンバーのMIIM_FTYPE値と fType メンバーのMFT_OWNERDRAW値を指定します。

MENUITEMINFO 構造体の適切なメンバーを設定することで、項目データと呼ばれるアプリケーション定義値を各メニュー項目に関連付けることができます。 これを行うには、fMask メンバーのMIIM_DATA値と dwItemData メンバーのアプリケーション定義値を指定します。

項目データは任意の種類のメニュー項目と共に使用できますが、所有者が描画した項目に特に役立ちます。 たとえば、構造体にメニュー項目の描画に使用される情報が含まれているとします。 アプリケーションは、メニュー項目の項目データを使用して、構造体へのポインターを格納する場合があります。 項目データは、WM_MEASUREITEMメッセージと WM_DRAWITEM メッセージを含むメニューの所有者ウィンドウ 送信されます。 メニューの項目データをいつでも取得するには、 GetMenuItemInfo 関数を使用します。

以前のバージョンのシステム用に作成されたアプリケーションは、AppendMenu、InsertMenu、または ModifyMenu引き続き呼び出して、所有者が描画したメニュー項目にMF_OWNERDRAW フラグを割り当てることができます。

これら 3 つの関数のいずれかを呼び出すときに、 lpNewItem パラメーターとして値を渡すことができます。 この値は、アプリケーションにとって意味のある情報を表し、アイテムを表示するときにアプリケーションで使用できる情報を表すことができます。 たとえば、 の値には構造体へのポインターを含める場合があります。構造には、テキスト文字列と、アプリケーションが文字列の描画に使用する論理フォントへのハンドルが含まれる場合があります。

Owner-Drawn メニューとWM_MEASUREITEM メッセージ

システムは、所有者が描画したメニュー項目を初めて表示する前に、項目のメニューを所有するウィンドウのウィンドウ プロシージャに WM_MEASUREITEM メッセージを送信します。 このメッセージには、アイテムを識別し、アプリケーションが割り当てたアイテム データを含む MEASUREITEMSTRUCT 構造体へのポインターが含まれています。 ウィンドウ プロシージャは、メッセージの処理から戻る前に、構造体の itemWidth メンバーと itemHeight メンバーを入力する必要があります。 システムは、アプリケーションがメニュー項目を描画する外接する四角形を作成するときに、これらのメンバーの情報を使用します。 また、この情報を使用して、ユーザーがいつアイテムを選択しているかを検出します。

Owner-Drawn メニューとWM_DRAWITEM メッセージ

アイテムを描画する必要がある場合 (たとえば、最初に表示されたときやユーザーが選択したときなど)、メニューの所有者ウィンドウのウィンドウ プロシージャに WM_DRAWITEM メッセージが送信されます。 このメッセージには DRAWITEMSTRUCT 構造体へのポインターが含まれます。この構造体には、アプリケーションによって割り当てられたアイテム データを含む、アイテムに関する情報が含まれます。 さらに、 DRAWITEMSTRUCT には、アイテムの状態 (灰色または選択されているかどうかなど) を示すフラグと、アプリケーションがアイテムの描画に使用する境界四角形とデバイス コンテキストが含まれます。

アプリケーションでは、 WM_DRAWITEM メッセージの処理中に次の操作を行う必要があります。

  • 必要な図面の種類を決定します。 そのためには、DRAWITEMSTRUCT 構造体の itemAction メンバーをチェックします。
  • DRAWITEMSTRUCT 構造体から取得した外接する四角形とデバイス コンテキストを使用して、メニュー項目を適切に描画します。 アプリケーションは、外接する四角形内でのみ描画する必要があります。 パフォーマンス上の理由から、四角形の外側に描画されるイメージの一部はクリップされません。
  • メニュー項目のデバイス コンテキストで選択されているすべての GDI オブジェクトを復元します。

ユーザーがメニュー項目を選択すると、DRAWITEMSTRUCT 構造体の itemAction メンバーがODA_SELECT値に設定され、itemState メンバーのODS_SELECTED値が設定されます。 これは、メニュー項目を再描画して選択されていることを示すアプリケーションの手掛かりです。

Owner-Drawn メニューとWM_MENUCHAR メッセージ

所有者が描画したメニュー以外のメニューでは、メニュー文字列の文字の横にアンダースコアを挿入することで、メニューニーモニックを指定できます。 これにより、ユーザーは Alt キーを押し、メニューニーモニック文字を押してメニューを選択できます。 ただし、所有者が描画したメニューでは、メニューニーモニックをこの方法で指定することはできません。 代わりに、アプリケーションで WM_MENUCHAR メッセージを処理して、所有者が描画したメニューにメニュー ニーモニックを提供する必要があります。

WM_MENUCHAR メッセージは、ユーザーが現在のメニューの定義済みのニーモニックのいずれにも一致しないメニュー ニーモニックを入力すると送信されます。 wParam に含まれる値は、ユーザーが Alt キーで押したキーに対応する ASCII 文字を指定します。 wParam の下位ワードは、選択したメニューの種類を指定し、次のいずれかの値を指定できます。

  • 現在のメニューがサブメニューの場合は、MF_POPUPします。
  • メニュー がシステム メニューの場合は、MF_SYSMENUします。

wParam の上位ワードには、現在のメニューへのメニュー ハンドルが含まれています。 所有者が描画したメニューを含むウィンドウでは、次のように WM_MENUCHAR 処理できます。

   case WM_MENUCHAR:
      nIndex = Determine index of menu item to be selected from
               character that was typed and handle to the current
               menu.
      return MAKELRESULT(nIndex, 2);

戻り値の上位ワードの 2 つは、戻り値の下位ワードに、選択するメニュー項目の 0 から始まるインデックスが含まれていることをシステムに通知します。

次の定数は、 WM_MENUCHAR メッセージから返される可能性のある値に対応しています。

定数 説明
MNC_IGNORE 0 システムは、ユーザーが押した文字を破棄し、システム スピーカーに短いビープ音を作成する必要があります。
MNC_CLOSE 1 システムはアクティブなメニューを閉じる必要があります。
MNC_EXECUTE 2 戻り値の下位ワードで指定された項目が選択されます。 所有者ウィンドウは 、WM_COMMAND メッセージを受け取ります。
MNC_SELECT 3 戻り値の下位ワードで指定された項目が選択されます。

 

Menu-Itemテキスト文字列のフォントを設定する

このトピックには、メニューで所有者が描画したメニュー項目を使用するアプリケーションの例が含まれています。 メニューには現在のフォントの属性を設定する項目が含まれており、項目は適切なフォント属性を使用して表示されます。

リソース定義ファイルでメニューを定義する方法を次に示します。 [標準]、[太字]、[斜体]、[下線] メニュー項目の文字列は実行時に割り当てられるため、リソース定義ファイルでは文字列が空になります。

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 構造体にコピーします。 幅と高さの値を使用して、メニューのサイズが計算されます。

WM_DRAWITEM メッセージの処理中に、メニュー項目の文字列は、チェックマークビットマップの文字列の横にスペースが残って描画されます。 ユーザーがアイテムを選択すると、選択したテキストと背景色がアイテムの描画に使用されます。

LRESULT APIENTRY MainWndProc(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 to 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;  // pointer to item of data             
    LPDRAWITEMSTRUCT lpdis;     // pointer to item drawing data        
    HDC hdc;                    // handle to screen DC                
    SIZE size;                  // menu-item text extents             
    WORD wCheckX;               // check-mark width                   
    int nTextX;                 // width of menu item                 
    int nTextY;                 // height of menu item                
    int i;                      // loop counter                       
    HFONT hfontOld;             // handle to old font                 
    BOOL fSelected = FALSE;     // menu-item selection flag
    size_t * pcch;
    HRESULT hResult;           
 
    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 for and font handle to 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.
            
            hResult = StringCchLength(pmyitem->psz,STRSAFE_MAX_CCH, pcch);
            if (FAILED(hResult))
            {
            // Add code to fail as securely as possible.
                return;
            } 
 
            GetTextExtentPoint32(hdc, pmyitem->psz, 
                *pcch, &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. 
 
            wCheckX = GetSystemMetrics(SM_CXMENUCHECK); 
            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);
            
            hResult = StringCchLength(pmyitem->psz,STRSAFE_MAX_CCH, pcch);
            if (FAILED(hResult))
            {
            // Add code to fail as securely as possible.
                return;
            } 
 
            ExtTextOut(lpdis->hDC, nTextX, nTextY, ETO_OPAQUE, 
                &lpdis->rcItem, pmyitem->psz, 
                *pcch, 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(int fnFont) 
{ 
    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); 
} 

メニュー項目のOwner-Drawn例

このトピックの例では、メニューの所有者が描画したメニュー項目を使用します。 メニュー項目は特定のフォント属性を選択し、アプリケーションは対応する属性を持つフォントを使用して各メニュー項目を表示します。 たとえば、[ 斜体 ] メニュー項目は斜体フォントで表示されます。 メニュー バーの [文字 ] メニュー名によってメニューが開きます。

メニュー バーとドロップダウン メニューは、最初は拡張メニュー テンプレート リソースによって定義されます。 メニュー テンプレートでは所有者が描画した項目を指定できないため、メニューには最初に、"Regular"、"Bold"、"Italic"、"Underline" という文字列を含む 4 つのテキスト メニュー項目が含まれています。アプリケーションのウィンドウ プロシージャは、 WM_CREATE メッセージを処理するときに、これらを所有者が描画したアイテムに変更します。 WM_CREATE メッセージを受信すると、ウィンドウ プロシージャはアプリケーション定義の OnCreate 関数を呼び出し、メニュー項目ごとに次の手順を実行します。

  • アプリケーション定義の MYITEM 構造体を割り当てます。
  • メニュー項目のテキストを取得し、アプリケーション定義の MYITEM 構造体に保存します。
  • メニュー項目の表示に使用するフォントを作成し、そのハンドルをアプリケーション定義の MYITEM 構造体に保存します。
  • メニュー項目の種類を MFT_OWNERDRAW に変更し、アプリケーション定義の MYITEM 構造体へのポインターを項目データとして保存します。

各アプリケーション定義 MYITEM 構造体へのポインターは項目データとして保存されるため、対応するメニュー項目の WM_MEASUREITEM メッセージとWM_DRAWITEMメッセージと共 ウィンドウ プロシージャに渡されます。 ポインターは、MEASUREITEMSTRUCT 構造体と DRAWITEMSTRUCT 構造体の両方の itemData メンバー含まれています。

WM_MEASUREITEMメッセージは、最初に表示されるときに、所有者が描画したメニュー項目ごとに送信されます。 アプリケーションは、メニュー項目のフォントをデバイス コンテキストに選択し、そのフォントでメニュー項目のテキストを表示するために必要な領域を決定することで、このメッセージを処理します。 フォントとメニュー項目のテキストは、両方ともメニュー項目の MYITEM 構造 (アプリケーションによって定義される構造体) で指定されます。 アプリケーションは、GetTextExtentPoint32 関数を使用してテキストのサイズを決定します。

ウィンドウ プロシージャは、メニュー項目のテキストを適切なフォントで表示することで、 WM_DRAWITEM メッセージを処理します。 フォントとメニュー項目のテキストは、両方ともメニュー項目の MYITEM 構造によって指定されます。 アプリケーションは、メニュー項目の状態に適したテキストと背景色を選択します。

ウィンドウ プロシージャは 、WM_DESTROY メッセージを処理してフォントを破棄し、メモリを解放します。 アプリケーションはフォントを削除し、各メニュー項目のアプリケーション定義 MYITEM 構造体を解放します。

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

// Menu-item identifiers for the Character menu 
 
#define IDM_CHARACTER 10 
#define IDM_REGULAR   11 
#define IDM_BOLD      12 
#define IDM_ITALIC    13 
#define IDM_UNDERLINE 14 
 
// Structure associated with menu items 
 
typedef struct tagMYITEM 
{ 
    HFONT hfont; 
    int   cchItemText; 
    char  szItemText[1]; 
} MYITEM; 
 
#define CCH_MAXITEMTEXT 256 
 

アプリケーションのウィンドウ プロシージャとそれに関連する関数の関連部分を次に示します。

LRESULT CALLBACK MainWindowProc( 
        HWND hwnd, 
        UINT uMsg, 
        WPARAM wParam, 
        LPARAM lParam 
        ) 
{ 
    switch (uMsg) 
    { 
        case WM_CREATE: 
            if (!OnCreate(hwnd)) 
                return -1; 
            break; 
 
        case WM_DESTROY: 
            OnDestroy(hwnd); 
            PostQuitMessage(0); 
            break; 
 
        case WM_MEASUREITEM: 
            OnMeasureItem(hwnd, (LPMEASUREITEMSTRUCT) lParam); 
            return TRUE; 
 
        case WM_DRAWITEM: 
            OnDrawItem(hwnd, (LPDRAWITEMSTRUCT) lParam); 
            return TRUE; 
 
        // Additional message processing goes here. 
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return 0; 
} 
 
 
BOOL WINAPI OnCreate(HWND hwnd) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
    UINT uID; 
    MYITEM *pMyItem; 
 
    // Get a handle to the pop-up menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Modify each menu item. Assume that the IDs IDM_REGULAR 
    // through IDM_UNDERLINE are consecutive numbers. 
 
    for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) 
    { 
         // Allocate an item structure, leaving space for a 
         // string of up to CCH_MAXITEMTEXT characters. 
 
        pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED, 
                sizeof(MYITEM) + CCH_MAXITEMTEXT); 
 
        // Save the item text in the item structure. 
 
        mii.fMask = MIIM_STRING; 
        mii.dwTypeData = pMyItem->szItemText; 
        mii.cch = CCH_MAXITEMTEXT; 
        GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
        pMyItem->cchItemText = mii.cch; 
 
        // Reallocate the structure to the minimum required size. 
 
        pMyItem = (MYITEM *) LocalReAlloc(pMyItem, 
                sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE); 
 
        // Create the font used to draw the item. 
 
        pMyItem->hfont = CreateMenuItemFont(uID); 
 
        // Change the item to an owner-drawn item, and save 
        // the address of the item structure as item data. 
 
        mii.fMask = MIIM_FTYPE | MIIM_DATA; 
        mii.fType = MFT_OWNERDRAW; 
        mii.dwItemData = (ULONG_PTR) pMyItem; 
        SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
    } 
    return TRUE; 
} 
 
HFONT CreateMenuItemFont(UINT uID) 
{ 
    LOGFONT lf;
    HRESULT hr; 
 
    ZeroMemory(&lf, sizeof(lf)); 
    lf.lfHeight = 20; 
    hr = StringCchCopy(lf.lfFaceName, 32, "Times New Roman");
    if (FAILED(hr))
    {
    // TODO: writer error handler
    } 
 
    switch (uID) 
    { 
        case IDM_BOLD: 
            lf.lfWeight = FW_HEAVY; 
            break; 
 
        case IDM_ITALIC: 
            lf.lfItalic = TRUE; 
            break; 
 
        case IDM_UNDERLINE: 
            lf.lfUnderline = TRUE; 
            break; 
    } 
    return CreateFontIndirect(&lf); 
} 
 
VOID WINAPI OnDestroy(HWND hwnd) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
    UINT uID; 
    MYITEM *pMyItem; 
 
    // Get a handle to the menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get  
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Free resources associated with each menu item. 
 
    for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) 
    { 
        // Get the item data. 
 
        mii.fMask = MIIM_DATA; 
        GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
        pMyItem = (MYITEM *) mii.dwItemData; 
 
        // Destroy the font and free the item structure. 
 
        DeleteObject(pMyItem->hfont); 
        LocalFree(pMyItem); 
    } 
} 
 
VOID WINAPI OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis) 
{ 
    MYITEM *pMyItem = (MYITEM *) lpmis->itemData; 
    HDC hdc = GetDC(hwnd); 
    HFONT hfntOld = (HFONT)SelectObject(hdc, pMyItem->hfont); 
    SIZE size; 
 
    GetTextExtentPoint32(hdc, pMyItem->szItemText, 
            pMyItem->cchItemText, &size); 
 
    lpmis->itemWidth = size.cx; 
    lpmis->itemHeight = size.cy; 
 
    SelectObject(hdc, hfntOld); 
    ReleaseDC(hwnd, hdc); 
} 
 
VOID WINAPI OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis) 
{ 
    MYITEM *pMyItem = (MYITEM *) lpdis->itemData; 
    COLORREF clrPrevText, clrPrevBkgnd; 
    HFONT hfntPrev; 
    int x, y; 
 
    // Set the appropriate foreground and background colors. 
 
    if (lpdis->itemState & ODS_SELECTED) 
    { 
        clrPrevText = SetTextColor(lpdis->hDC, 
                GetSysColor(COLOR_HIGHLIGHTTEXT)); 
        clrPrevBkgnd = SetBkColor(lpdis->hDC, 
                GetSysColor(COLOR_HIGHLIGHT)); 
    } 
    else 
    { 
        clrPrevText = SetTextColor(lpdis->hDC, 
                GetSysColor(COLOR_MENUTEXT)); 
        clrPrevBkgnd = SetBkColor(lpdis->hDC, 
                GetSysColor(COLOR_MENU)); 
    } 
 
    // Determine where to draw and leave space for a check mark. 
 
    x = lpdis->rcItem.left; 
    y = lpdis->rcItem.top; 
    x += GetSystemMetrics(SM_CXMENUCHECK); 
 
    // Select the font and draw the text. 
 
    hfntPrev = (HFONT)SelectObject(lpdis->hDC, pMyItem->hfont); 
    ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE, 
            &lpdis->rcItem, pMyItem->szItemText, 
            pMyItem->cchItemText, NULL); 
 
    // Restore the original font and colors. 
 
    SelectObject(lpdis->hDC, hfntPrev); 
    SetTextColor(lpdis->hDC, clrPrevText); 
    SetBkColor(lpdis->hDC, clrPrevBkgnd); 
} 

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

システムには、選択したメニュー項目の横に表示するための既定のチェックマーク ビットマップが用意されています。 既定のチェックマークビットマップを置き換えるビットマップのペアを指定することで、個々のメニュー項目をカスタマイズできます。 項目が選択されている場合は 1 つのビットマップが表示され、クリアされている場合はもう 1 つのビットマップが表示されます。 このセクションでは、カスタム チェック マーク ビットマップの作成と使用に関連する手順について説明します。

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

カスタム チェック マーク ビットマップは、既定のチェックマーク ビットマップと同じサイズにする必要があります。 GetSystemMetrics 関数を呼び出すことで、ビットマップの既定のチェックマーク サイズを取得できます。 この関数の戻り値の下位ワードは幅を指定します。高い単語は高さを指定します。

ビットマップ リソースを使用して、チェックマークビットマップを提供できます。 ただし、必要なビットマップ サイズは表示の種類によって異なるため、 StretchBlt 関数を使用して実行時にビットマップのサイズを変更する必要がある場合があります。 ビットマップによっては、サイズ変更によって引き起こされる歪みによって、許容できない結果が生じる可能性があります。

ビットマップ リソースを使用する代わりに、GDI 関数を使用して実行時にビットマップを作成できます。

実行時にビットマップを作成するには

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

    関数の hdc パラメーターでは、 NULL または関数からの戻り値を指定できます。 CreateCompatibleDC は、互換性のあるデバイス コンテキストへのハンドルを返します。

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

    この関数の nWidth パラメーターと nHeight パラメーターは、ビットマップのサイズを設定します。 GetSystemMetrics 関数によって返される幅と高さの情報を指定する必要があります。

    Note

    CreateBitmap 関数を使用して、モノクロ ビットマップを作成することもできます。

     

  3. SelectObject 関数を使用して、互換性のあるデバイス コンテキストにビットマップを選択します。

  4. EllipseLineTo などの GDI 描画関数を使用してビットマップにイメージを描画するか、BitBlt や StretchBlt などの関数を使用してイメージをビットマップにコピーします。

詳細については、「 ビットマップ」を参照してください。

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

ビットマップのハンドルを SetMenuItemBitmaps 関数に渡すことで、チェックマークビットマップのペアをメニュー項目に関連付けます。 hBitmapUnchecked パラメーターはクリア ビットマップを識別し、hBitmapChecked パラメーターは選択したビットマップを識別します。 メニュー項目から一方または両方のチェックマークを削除する場合は、hBitmapUnchecked パラメーターまたは hBitmapChecked パラメーター、またはその両方を NULL に設定できます。

Check-mark 属性の設定

CheckMenuItem 関数は、メニュー項目のチェックマーク属性を選択またはクリアに設定します。 MF_CHECKED値を指定して、チェックマーク属性を選択に設定し、MF_UNCHECKED値をクリアに設定できます。

SetMenuItemInfo 関数を使用して、メニュー項目のチェック状態を設定することもできます。

メニュー項目のグループは、相互に排他的なオプションのセットを表す場合があります。 CheckMenuRadioItem 関数を使用すると、グループ内の他のすべてのメニュー項目からチェックマークを同時に削除しながら、1 つのメニュー項目をチェックできます。

メニューのチェック ボックスをシミュレートする

このトピックでは、メニューのチェック ボックスをシミュレートする方法を示す例を示します。 この例には、現在のフォントの太字、斜体、下線の各属性を設定できる項目を含む [文字] メニューが含まれています。 フォント属性が有効な場合は、対応するメニュー項目の横のチェック ボックスにチェックマークが表示されます。それ以外の場合は、項目の横に空のチェック ボックスが表示されます。

この例では、既定のチェックマーク ビットマップを 2 つのビットマップに置き換えます。選択したチェック ボックスを持つビットマップと、空のボックスを持つビットマップです。 項目のチェックマーク属性がMF_CHECKEDに設定されている場合、選択したチェック ボックスのビットマップが [太字]、[斜体]、または [下線] メニュー項目の横に表示されます。 チェック マーク属性が MF_UNCHECKED に設定されている場合、クリアまたは空のチェック ボックスビットマップが表示されます。

システムには、チェック ボックスとラジオ ボタンに使用されるイメージを含む定義済みのビットマップが用意されています。 この例では、選択したチェックボックスと空のチェック ボックスを分離し、それらを 2 つの別々のビットマップにコピーし、[文字] メニューの項目の選択およびクリアされたビットマップとして使用します。

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

リソース定義ファイルの次の部分は、[ 文字 ] メニューのメニュー項目の定義方法を示しています。 最初はフォント属性が有効でないため、標準アイテムのチェックマーク属性は選択済みに設定され、既定では残りのアイテムのチェックマーク属性はクリアに設定されます。

#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); 

次の例は、ウィンドウ プロシージャの一部を示しています。この部分では、チェックマークビットマップを作成し、太字斜体下線の各メニュー項目のチェックマーク属性を設定し、チェックマークのビットマップを破棄します。

LRESULT APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
 
    static HBITMAP hbmpCheck;   // handle to checked bitmap    
    static HBITMAP hbmpUncheck; // handle to unchecked bitmap  
    static HMENU hmenu;         // handle to 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 
                    // checkmarks 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(UINT fuCheck) 
{ 
    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 to check-box bitmap        
    BITMAP bmCheckbox;      // structure for bitmap data         
    HBITMAP hbmpSourceOld;  // handle to original source bitmap  
    HBITMAP hbmpTargetOld;  // handle to original target bitmap  
    HBITMAP hbmpCheck;      // handle to check-mark bitmap       
    RECT rc;                // rectangle for check-box 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 system default check-mark bitmap and 
    // create a compatible bitmap of the same size. 
 
    wBitmapX = GetSystemMetrics(SM_CXMENUCHECK); 
    wBitmapY = GetSystemMetrics(SM_CYMENUCHECK); 
 
    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 a handle to the new check-mark bitmap.  
 
    return hbmpCheck; 
} 
 
 
BYTE CheckOrUncheckMenuItem(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 checkmarks 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; 
    } 
} 

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

このトピックの例では、カスタム チェックマーク ビットマップを 2 つのメニューのメニュー項目に割り当てます。 最初のメニューのメニュー項目では、文字属性 (太字、斜体、下線) を指定します。 各メニュー項目は、選択またはクリアできます。 これらのメニュー項目では、チェック ボックス コントロールの選択状態とクリア状態に似たチェックマークビットマップを使用します。

2 番目のメニューのメニュー項目は、段落の配置設定 (左、中央、右) を指定します。 これらのメニュー項目は、いつでも 1 つだけ選択されます。 これらのメニュー項目では、ラジオ ボタン コントロールの選択された状態とクリア状態に似たチェックマークビットマップを使用します。

ウィンドウ プロシージャは、アプリケーション定義の OnCreate 関数を呼び出して、 WM_CREATE メッセージを処理します。 OnCreateは、4 つのチェックマークビットマップを作成し、SetMenuItemBitmaps 関数を使用して適切なメニュー項目に割り当てます。

各ビットマップを作成するために、OnCreate は、ビットマップ固有の描画関数へのポインターを指定して、アプリケーション定義の CreateMenuBitmaps 関数を呼び出します。 CreateMenuBitmaps は、必要なサイズのモノクロ ビットマップを作成し、それをメモリ デバイス コンテキストに選択して、背景を消去します。 次に、指定した描画関数を呼び出して、前景を塗りつぶします。

アプリケーション定義の 4 つの描画関数は、DrawCheck、DrawUncheck、 DrawRadioCheck、DrawRadioUncheck です。 X、空の四角形、小さな塗りつぶされた楕円を含む楕円、空の楕円をそれぞれ使用して四角形を描画します。

ウィンドウ プロシージャは、チェックマークビットマップを削除することによって、WM_DESTROY メッセージを処理します。 GetMenuItemInfo 関数を使用して各ビットマップ ハンドルを取得し、その関数にハンドルを渡します。

ユーザーがメニュー項目を選択すると、 WM_COMMAND メッセージが所有者ウィンドウに送信されます。 [文字] メニューのメニュー項目の場合、ウィンドウ プロシージャはアプリケーション定義の CheckCharacterItem 関数を呼び出します。 [段落] メニューの項目の場合、ウィンドウ プロシージャはアプリケーション定義の CheckParagraphItem 関数を呼び出します。

[ 文字 ] メニューの各項目は、個別に選択およびクリアできます。 したがって、CheckCharacterItem は、指定したメニュー項目のチェック状態を切り替えるだけです。 最初に、この関数は GetMenuItemInfo 関数を呼び出して、現在のメニュー項目の状態を取得します。 次に、 MFS_CHECKED 状態フラグを切り替え、 SetMenuItemInfo 関数を呼び出して新しい状態を設定します。

文字属性とは異なり、一度に選択できる段落の配置は 1 つだけです。 したがって、CheckParagraphItem は、指定したメニュー項目をチェックし、メニュー上の他のすべての項目からチェックマークを削除します。 これを行うには、 CheckMenuRadioItem 関数を呼び出します。

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

// Menu-item identifiers for the Character menu 
 
#define IDM_CHARACTER 10 
#define IDM_BOLD      11 
#define IDM_ITALIC    12 
#define IDM_UNDERLINE 13 
 
// Menu-item identifiers for the Paragraph menu 
 
#define IDM_PARAGRAPH 20 
#define IDM_LEFT      21 
#define IDM_CENTER    22 
#define IDM_RIGHT     23 
 
// Function-pointer type for drawing functions 
 
typedef VOID (WINAPI * DRAWFUNC)(HDC hdc, SIZE size); 
 

アプリケーションのウィンドウ プロシージャと関連する関数の関連部分を次に示します。

LRESULT CALLBACK MainWindowProc( 
        HWND hwnd, 
        UINT uMsg, 
        WPARAM wParam, 
        LPARAM lParam 
        ) 
{ 
    switch (uMsg) 
    { 
        case WM_CREATE: 
            if (!OnCreate(hwnd)) 
                return -1; 
            break; 
 
        case WM_DESTROY: 
            OnDestroy(hwnd); 
            PostQuitMessage(0); 
            break; 
 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDM_BOLD: 
                case IDM_ITALIC: 
                case IDM_UNDERLINE: 
                    CheckCharacterItem(hwnd, LOWORD(wParam)); 
                    break; 
 
                case IDM_LEFT: 
                case IDM_CENTER: 
                case IDM_RIGHT: 
                    CheckParagraphItem(hwnd, LOWORD(wParam)); 
                    break; 
 
                // Process other commands here. 
 
            } 
            break; 
 
        // Process other messages here. 
 
        default: 
            return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    return 0; 
} 
 
VOID WINAPI CheckCharacterItem(HWND hwnd, UINT uID) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
 
    // Get a handle to the Character menu. 
 
    mii.fMask = MIIM_SUBMENU;  // information to get 
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Get the state of the specified menu item. 
 
    mii.fMask = MIIM_STATE;    // information to get 
    GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
 
    // Toggle the checked state. 
 
    mii.fState ^= MFS_CHECKED; 
    SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii); 
} 
 
VOID WINAPI CheckParagraphItem(HWND hwnd, UINT uID) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
 
    // Get a handle to the Paragraph menu. 
 
    mii.fMask = MIIM_SUBMENU;  // information to get 
    GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Check the specified item and uncheck all the others. 
 
    CheckMenuRadioItem( 
            hmenuPopup,         // handle to menu 
            IDM_LEFT,           // first item in range 
            IDM_RIGHT,          // last item in range 
            uID,                // item to check 
            MF_BYCOMMAND        // IDs, not positions 
            ); 
} 
 
BOOL WINAPI OnCreate(HWND hwnd) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
    UINT uID; 
    HBITMAP hbmChecked; 
    HBITMAP hbmUnchecked; 
 
    // Get a handle to the Character menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Create the checked and unchecked bitmaps. 
 
    hbmChecked = CreateMenuBitmap(DrawCheck); 
    hbmUnchecked = CreateMenuBitmap(DrawUncheck); 
 
    // Set the check-mark bitmaps for each menu item. 
 
    for (uID = IDM_BOLD; uID <= IDM_UNDERLINE; uID++) 
    { 
        SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND, 
                hbmUnchecked, hbmChecked); 
    } 
 
    // Get a handle to the Paragraph pop-up menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Create the checked and unchecked bitmaps. 
 
    hbmChecked = CreateMenuBitmap(DrawRadioCheck); 
    hbmUnchecked = CreateMenuBitmap(DrawRadioUncheck); 
 
    // Set the check-mark bitmaps for each menu item. 
 
    for (uID = IDM_LEFT; uID <= IDM_RIGHT; uID++) 
    { 
        SetMenuItemBitmaps(hmenuPopup, uID, MF_BYCOMMAND, 
                hbmUnchecked, hbmChecked); 
    } 
 
    // Initially check the IDM_LEFT paragraph item. 
 
    CheckMenuRadioItem(hmenuPopup, IDM_LEFT, IDM_RIGHT, 
            IDM_LEFT, MF_BYCOMMAND); 
    return TRUE; 
} 
 
HBITMAP WINAPI CreateMenuBitmap(DRAWFUNC lpfnDraw) 
{ 
    // Create a DC compatible with the desktop window's DC. 
 
    HWND hwndDesktop = GetDesktopWindow(); 
    HDC hdcDesktop = GetDC(hwndDesktop); 
    HDC hdcMem = CreateCompatibleDC(hdcDesktop); 
 
    // Determine the required bitmap size. 
 
    SIZE size = { GetSystemMetrics(SM_CXMENUCHECK), 
                  GetSystemMetrics(SM_CYMENUCHECK) }; 
 
    // Create a monochrome bitmap and select it. 
 
    HBITMAP hbm = CreateBitmap(size.cx, size.cy, 1, 1, NULL); 
    HBITMAP hbmOld = SelectObject(hdcMem, hbm); 
 
    // Erase the background and call the drawing function. 
 
    PatBlt(hdcMem, 0, 0, size.cx, size.cy, WHITENESS); 
    (*lpfnDraw)(hdcMem, size); 
 
    // Clean up. 
 
    SelectObject(hdcMem, hbmOld); 
    DeleteDC(hdcMem); 
    ReleaseDC(hwndDesktop, hdcDesktop); 
    return hbm; 
} 
 
VOID WINAPI DrawCheck(HDC hdc, SIZE size) 
{ 
    HBRUSH hbrOld; 
    hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
    Rectangle(hdc, 0, 0, size.cx, size.cy); 
    MoveToEx(hdc, 0, 0, NULL); 
    LineTo(hdc, size.cx, size.cy); 
    MoveToEx(hdc, 0, size.cy - 1, NULL); 
    LineTo(hdc, size.cx - 1, 0); 
    SelectObject(hdc, hbrOld); 
} 
 
VOID WINAPI DrawUncheck(HDC hdc, SIZE size) 
{ 
    HBRUSH hbrOld; 
    hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
    Rectangle(hdc, 0, 0, size.cx, size.cy); 
    SelectObject(hdc, hbrOld); 
} 
 
VOID WINAPI DrawRadioCheck(HDC hdc, SIZE size) 
{ 
    HBRUSH hbrOld; 
    hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
    Ellipse(hdc, 0, 0, size.cx, size.cy); 
    SelectObject(hdc, GetStockObject(BLACK_BRUSH)); 
    Ellipse(hdc, 2, 2, size.cx - 2, size.cy - 2); 
    SelectObject(hdc, hbrOld); 
} 
 
VOID WINAPI DrawRadioUncheck(HDC hdc, SIZE size) 
{ 
    HBRUSH hbrOld; 
    hbrOld = SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
    Ellipse(hdc, 0, 0, size.cx, size.cy); 
    SelectObject(hdc, hbrOld); 
} 
 
VOID WINAPI OnDestroy(HWND hwnd) 
{ 
    HMENU hmenuBar = GetMenu(hwnd); 
    HMENU hmenuPopup; 
    MENUITEMINFO mii; 
 
    // Get a handle to the Character menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Get the check-mark bitmaps and delete them. 
 
    mii.fMask = MIIM_CHECKMARKS; 
    GetMenuItemInfo(hmenuPopup, IDM_BOLD, FALSE, &mii); 
    DeleteObject(mii.hbmpChecked); 
    DeleteObject(mii.hbmpUnchecked); 
 
    // Get a handle to the Paragraph menu. 
 
    mii.fMask = MIIM_SUBMENU;     // information to get 
    GetMenuItemInfo(hmenuBar, IDM_PARAGRAPH, FALSE, &mii); 
    hmenuPopup = mii.hSubMenu; 
 
    // Get the check-mark bitmaps and delete them. 
 
    mii.fMask = MIIM_CHECKMARKS; 
    GetMenuItemInfo(hmenuPopup, IDM_LEFT, FALSE, &mii); 
    DeleteObject(mii.hbmpChecked); 
    DeleteObject(mii.hbmpUnchecked); 
}