Menggunakan Menu

Bagian ini menjelaskan tugas-tugas berikut:

Menggunakan Sumber Daya Menu-Template

Anda biasanya menyertakan menu dalam aplikasi dengan membuat sumber daya templat menu lalu memuat menu pada durasi. Bagian ini menjelaskan format templat menu, dan menjelaskan cara memuat sumber daya templat menu dan menggunakannya di aplikasi Anda. Untuk informasi tentang membuat sumber daya templat menu, lihat dokumentasi yang disertakan dengan alat pengembangan Anda.

Format Menu-Template yang Diperluas

Format templat menu yang diperluas mendukung fungsionalitas menu tambahan. Seperti sumber daya templat menu standar, sumber daya templat menu yang diperluas memiliki jenis sumber daya RT_MENU . Sistem membedakan dua format sumber daya dengan nomor versi, yang merupakan anggota pertama header sumber daya.

Templat menu yang diperluas terdiri dari struktur MENUEX_TEMPLATE_HEADER diikuti oleh satu lagi struktur definisi item MENUEX_TEMPLATE_ITEM .

Format Menu-Template Lama

Templat menu lama (Microsoft Windows NT 3.51 dan yang lebih lama) menentukan menu, tetapi tidak mendukung fungsionalitas menu baru. Sumber daya templat menu lama memiliki jenis sumber daya RT_MENU .

Templat menu lama terdiri dari struktur MENUITEMTEMPLATEHEADER diikuti oleh satu atau beberapa struktur MENUITEMTEMPLATE .

Memuat Sumber Daya Menu-Template

Untuk memuat sumber daya templat menu, gunakan fungsi LoadMenu , menentukan handel ke modul yang berisi sumber daya dan pengidentifikasi templat menu. Fungsi LoadMenu mengembalikan handel menu yang dapat Anda gunakan untuk menetapkan menu ke jendela. Jendela ini menjadi jendela pemilik menu, menerima semua pesan yang dihasilkan oleh menu.

Untuk membuat menu dari templat menu yang sudah ada dalam memori, gunakan fungsi LoadMenuIndirect . Ini berguna jika aplikasi Anda menghasilkan templat menu secara dinamis.

Untuk menetapkan menu ke jendela, gunakan fungsi SetMenu atau tentukan handel menu di parameter hMenu fungsi CreateWindowEx saat membuat jendela. Cara lain Anda dapat menetapkan menu ke jendela adalah dengan menentukan templat menu saat Anda mendaftarkan kelas jendela; templat mengidentifikasi menu yang ditentukan sebagai menu kelas untuk kelas jendela tersebut.

Agar sistem secara otomatis menetapkan menu tertentu ke jendela, tentukan templat menu saat Anda mendaftarkan kelas jendela. Templat mengidentifikasi menu yang ditentukan sebagai menu kelas untuk kelas jendela tersebut. Kemudian, saat Anda membuat jendela kelas yang ditentukan, sistem secara otomatis menetapkan menu yang ditentukan ke jendela.

Anda tidak dapat menetapkan menu ke jendela yang merupakan jendela anak.

Untuk membuat menu kelas, sertakan pengidentifikasi sumber daya templat menu sebagai anggota lpszMenuName dari struktur WNDCLASS lalu teruskan penunjuk ke struktur ke fungsi RegisterClass .

Membuat Menu Kelas

Contoh berikut menunjukkan cara membuat menu kelas untuk aplikasi, membuat jendela yang menggunakan menu kelas, dan memproses perintah menu di prosedur jendela.

Berikut ini adalah bagian yang relevan dari file header aplikasi:

// Menu-template resource identifier 
 
#define IDM_MYMENURESOURCE   3

Berikut ini adalah bagian yang relevan dari aplikasi itu sendiri:

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; 
} 

Membuat Menu Pintasan

Untuk menggunakan menu pintasan dalam aplikasi, teruskan handelnya ke fungsi TrackPopupMenuEx . Aplikasi biasanya memanggil TrackPopupMenuEx dalam prosedur jendela sebagai respons terhadap pesan buatan pengguna, seperti WM_LBUTTONDOWN atau WM_KEYDOWN.

Selain handel menu pop-up, TrackPopupMenuEx mengharuskan Anda menentukan handel ke jendela pemilik, posisi menu pintasan (dalam koordinat layar), dan tombol mouse yang dapat digunakan pengguna untuk memilih item.

Fungsi TrackPopupMenu yang lebih lama masih didukung, tetapi aplikasi baru harus menggunakan fungsi TrackPopupMenuEx . Fungsi TrackPopupMenuEx memerlukan parameter yang sama dengan TrackPopupMenu, tetapi juga memungkinkan Anda menentukan sebagian layar yang tidak boleh dikaburkan oleh menu. Aplikasi biasanya memanggil fungsi-fungsi ini dalam prosedur jendela saat memproses pesan WM_CONTEXTMENU .

Anda dapat menentukan posisi menu pintasan dengan menyediakan koordinat x dan y bersama dengan bendera TPM_CENTERALIGN, TPM_LEFTALIGN, atau TPM_RIGHTALIGN . Bendera menentukan posisi menu pintasan relatif terhadap koordinat x- dan y.

Anda harus mengizinkan pengguna untuk memilih item dari menu pintasan dengan menggunakan tombol mouse yang sama yang digunakan untuk menampilkan menu. Untuk melakukan ini, tentukan bendera TPM_LEFTBUTTON atau TPM_RIGHTBUTTON untuk menunjukkan tombol mouse mana yang dapat digunakan pengguna untuk memilih item menu.

Memproses Pesan WM_CONTEXTMENU

Pesan WM_CONTEXTMENU dihasilkan ketika prosedur jendela aplikasi meneruskan pesan WM_RBUTTONUP atau WM_NCRBUTTONUP ke fungsi DefWindowProc . Aplikasi dapat memproses pesan ini untuk menampilkan menu pintasan yang sesuai dengan bagian tertentu dari layarnya. Jika aplikasi tidak menampilkan menu pintasan, aplikasi harus meneruskan pesan ke DefWindowProc untuk penanganan default.

Berikut ini adalah contoh pemrosesan pesan WM_CONTEXTMENU karena mungkin muncul dalam prosedur jendela aplikasi. Kata-kata berurutan rendah dan berurutan tinggi dari parameter lParam menentukan koordinat layar mouse ketika tombol kanan mouse dilepaskan (perhatikan bahwa koordinat ini dapat mengambil nilai negatif pada sistem dengan beberapa monitor). Fungsi OnContextMenu yang ditentukan aplikasi mengembalikan TRUE jika menampilkan menu konteks, atau FALSE jika tidak.

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

Fungsi OnContextMenu yang ditentukan aplikasi berikut menampilkan menu pintasan jika posisi mouse yang ditentukan berada di dalam area klien jendela. Fungsi yang lebih canggih mungkin menampilkan salah satu dari beberapa menu yang berbeda, tergantung pada bagian area klien mana yang ditentukan. Untuk benar-benar menampilkan menu pintasan, contoh ini memanggil fungsi yang ditentukan aplikasi yang disebut DisplayContextMenu. Untuk deskripsi fungsi ini, lihat Menampilkan Menu Pintasan.

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; 
} 

Membuat Menu Font-Attributes Pintasan

Contoh di bagian ini berisi bagian kode dari aplikasi yang membuat dan menampilkan menu pintasan yang memungkinkan pengguna untuk mengatur font dan atribut font. Aplikasi menampilkan menu di area klien jendela utamanya setiap kali pengguna mengklik tombol kiri mouse.

Berikut adalah templat menu untuk menu pintasan yang disediakan dalam file definisi sumber daya aplikasi.

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 

Contoh berikut memberikan prosedur jendela dan fungsi pendukung yang digunakan untuk membuat dan menampilkan menu pintasan.

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

Menampilkan Menu Pintasan

Fungsi yang diperlihatkan dalam contoh berikut menampilkan menu pintasan.

Aplikasi ini mencakup sumber daya menu yang diidentifikasi oleh string "ShortcutExample." Bilah menu hanya berisi nama menu. Aplikasi ini menggunakan fungsi TrackPopupMenu untuk menampilkan menu yang terkait dengan item menu ini. (Bilah menu itu sendiri tidak ditampilkan karena TrackPopupMenu memerlukan handel ke menu, submenu, atau menu pintasan.)

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

Menggunakan bitmap Menu-Item

Sistem dapat menggunakan bitmap alih-alih string teks untuk menampilkan item menu. Untuk menggunakan bitmap, Anda harus mengatur bendera MIIM_BITMAP untuk item menu dan menentukan handel ke bitmap yang harus ditampilkan sistem untuk item menu di anggota hbmpItem dari struktur MENUITEMINFO . Bagian ini menjelaskan cara menggunakan bitmap untuk item menu.

Mengatur Bendera Jenis Bitmap

Bendera MIIM_BITMAP atau MF_BITMAP memberi tahu sistem untuk menggunakan bitmap daripada string teks untuk menampilkan item menu. Bendera MIIM_BITMAP atau MF_BITMAP item menu harus diatur pada durasi; Anda tidak dapat mengaturnya dalam file definisi sumber daya.

Untuk aplikasi baru, Anda dapat menggunakan fungsi SetMenuItemInfo atau InsertMenuItem untuk mengatur bendera jenis MIIM_BITMAP . Untuk mengubah item menu dari item teks menjadi item bitmap, gunakan SetMenuItemInfo. Untuk menambahkan item bitmap baru ke menu, gunakan fungsi InsertMenuItem .

Aplikasi yang ditulis untuk versi sistem yang lebih lama dapat terus menggunakan fungsi ModifikasiMenu, InsertMenu, atau AppendMenu untuk mengatur bendera MF_BITMAP . Untuk mengubah item menu dari item string teks menjadi item bitmap, gunakan ModifikasiMenu. Untuk menambahkan item bitmap baru ke menu, gunakan bendera MF_BITMAP dengan fungsi InsertMenu atau AppendMenu .

Membuat Bitmap

Saat Anda mengatur bendera jenis MIIM_BITMAP atau MF_BITMAP untuk item menu, Anda juga harus menentukan handel ke bitmap yang harus ditampilkan sistem untuk item menu. Anda dapat menyediakan bitmap sebagai sumber daya bitmap atau membuat bitmap pada durasi. Jika Anda menggunakan sumber daya bitmap, Anda dapat menggunakan fungsi LoadBitmap untuk memuat bitmap dan mendapatkan handelnya.

Untuk membuat bitmap pada durasi, gunakan fungsi Windows Graphics Device Interface (GDI). GDI menyediakan beberapa cara untuk membuat bitmap pada waktu proses, tetapi pengembang biasanya menggunakan metode berikut:

  1. Gunakan fungsi CreateCompatibleDC untuk membuat konteks perangkat yang kompatibel dengan konteks perangkat yang digunakan oleh jendela utama aplikasi.
  2. Gunakan fungsi CreateCompatibleBitmap untuk membuat bitmap yang kompatibel dengan jendela utama aplikasi atau gunakan fungsi CreateBitmap untuk membuat bitmap monokrom.
  3. Gunakan fungsi SelectObject untuk memilih bitmap ke dalam konteks perangkat yang kompatibel.
  4. Gunakan fungsi gambar GDI, seperti Elips dan LineTo, untuk menggambar gambar ke dalam bitmap.

Untuk informasi selengkapnya, lihat Bitmap.

Menambahkan Garis dan Grafik ke Menu

Contoh kode berikut menunjukkan cara membuat menu yang berisi bitmap item menu. Ini membuat dua menu. Yang pertama adalah menu Bagan yang berisi bitmap tiga item menu: bagan pai, bagan garis, dan bagan batang. Contoh menunjukkan cara memuat bitmap ini dari file sumber daya aplikasi, lalu menggunakan fungsi CreatePopupMenu dan AppendMenu untuk membuat menu dan item menu.

Menu kedua adalah menu Garis. Ini berisi bitmap yang menunjukkan gaya garis yang disediakan oleh pena yang telah ditentukan sebelumnya dalam sistem. Bitmap gaya garis dibuat pada durasi dengan menggunakan fungsi GDI.

Berikut adalah definisi sumber daya bitmap dalam file definisi sumber daya aplikasi.

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

Berikut adalah bagian yang relevan dari file header aplikasi.

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

Contoh berikut menunjukkan bagaimana menu dan bitmap item menu dibuat dalam aplikasi.

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; 
} 

Contoh Bitmap Menu-Item

Contoh dalam topik ini membuat dua menu, masing-masing berisi beberapa item menu bitmap. Untuk setiap menu, aplikasi menambahkan nama menu yang sesuai ke bilah menu jendela utama.

Menu pertama berisi item menu yang memperlihatkan masing-masing dari tiga jenis bagan: pai, garis, dan bilah. Bitmap untuk item menu ini didefinisikan sebagai sumber daya dan dimuat dengan menggunakan fungsi LoadBitmap . Terkait dengan menu ini adalah nama menu "Bagan" pada bilah menu.

Menu kedua berisi item menu yang memperlihatkan masing-masing dari lima gaya baris yang digunakan dengan fungsi CreatePen : PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, dan PS_DASHDOTDOT. Aplikasi membuat bitmap untuk item menu ini pada durasi menggunakan fungsi gambar GDI. Terkait dengan menu ini adalah nama menu Garis pada bilah menu.

Ditentukan dalam prosedur jendela aplikasi adalah dua array statis handel bitmap. Satu array berisi handel dari tiga bitmap yang digunakan untuk menu Bagan . Yang lain berisi handel dari lima bitmap yang digunakan untuk menu Garis . Saat memproses pesan WM_CREATE , prosedur jendela memuat bitmap bagan, membuat bitmap baris, lalu menambahkan item menu yang sesuai. Saat memproses pesan WM_DESTROY , prosedur jendela menghapus semua bitmap.

Berikut ini adalah bagian yang relevan dari file header aplikasi.

// 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 

Berikut ini adalah bagian yang relevan dari prosedur jendela. Prosedur jendela melakukan sebagian besar inisialisasinya dengan memanggil fungsi LoadChartBitmaps, CreateLineBitmaps, dan AddBitmapMenu yang ditentukan aplikasi, yang dijelaskan nanti dalam topik ini.

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; 
} 

Fungsi LoadChartBitmaps yang ditentukan aplikasi memuat sumber daya bitmap untuk menu bagan dengan memanggil fungsi LoadBitmap , sebagai berikut.

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

Fungsi CreateLineBitmaps yang ditentukan aplikasi membuat bitmap untuk menu Garis dengan menggunakan fungsi gambar GDI. Fungsi ini membuat konteks perangkat memori (DC) dengan properti yang sama dengan DC jendela desktop. Untuk setiap gaya garis, fungsi membuat bitmap, memilihnya ke dalam DC memori, dan menggambar di dalamnya.

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

Fungsi AddBitmapMenu yang ditentukan aplikasi membuat menu dan menambahkan jumlah item menu bitmap yang ditentukan ke dalamnya. Kemudian menambahkan nama menu yang sesuai ke bilah menu jendela yang ditentukan.

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

Membuat Item Menu Owner-Drawn

Jika Anda memerlukan kontrol penuh atas tampilan item menu, Anda dapat menggunakan item menu yang digambar pemilik di aplikasi Anda. Bagian ini menjelaskan langkah-langkah yang terlibat dalam membuat dan menggunakan item menu yang digambar pemilik.

Mengatur Bendera Owner-Drawn

Anda tidak dapat menentukan item menu yang digambar pemilik dalam file definisi sumber daya aplikasi Anda. Sebagai gantinya, Anda harus membuat item menu baru atau mengubah item yang sudah ada dengan menggunakan bendera menu MFT_OWNERDRAW .

Anda dapat menggunakan fungsi InsertMenuItem atau SetMenuItemInfo untuk menentukan item menu yang digambar pemilik. Gunakan InsertMenuItem untuk menyisipkan item menu baru pada posisi yang ditentukan di bilah menu atau menu. Gunakan SetMenuItemInfo untuk mengubah konten menu.

Saat memanggil kedua fungsi ini, Anda harus menentukan penunjuk ke struktur MENUITEMINFO , yang menentukan properti item menu baru atau properti yang ingin Anda ubah untuk item menu yang ada. Untuk membuat item sebagai item yang digambar pemilik, tentukan nilai MIIM_FTYPE untuk anggota fMask dan nilai MFT_OWNERDRAW untuk anggota fType .

Dengan mengatur anggota struktur MENUITEMINFO yang sesuai, Anda dapat mengaitkan nilai yang ditentukan aplikasi, yang disebut data item, dengan setiap item menu. Untuk melakukannya, tentukan nilai MIIM_DATA untuk anggota fMask dan nilai yang ditentukan aplikasi untuk anggota dwItemData .

Anda dapat menggunakan data item dengan semua jenis item menu, tetapi sangat berguna untuk item yang digambar pemilik. Misalnya, struktur berisi informasi yang digunakan untuk menggambar item menu. Aplikasi mungkin menggunakan data item untuk item menu untuk menyimpan penunjuk ke struktur. Data item dikirim ke jendela pemilik menu dengan pesan WM_MEASUREITEM dan WM_DRAWITEM . Untuk mengambil data item untuk menu kapan saja, gunakan fungsi GetMenuItemInfo .

Aplikasi yang ditulis untuk versi sistem yang lebih lama dapat terus memanggil AppendMenu, InsertMenu, atau ModifikasiMenu untuk menetapkan bendera MF_OWNERDRAW ke item menu yang digambar pemilik.

Saat Anda memanggil salah satu dari ketiga fungsi ini, Anda dapat meneruskan nilai sebagai parameter lpNewItem . Nilai ini dapat mewakili informasi apa pun yang bermakna bagi aplikasi Anda, dan itu akan tersedia untuk aplikasi Anda ketika item akan ditampilkan. Misalnya, nilai dapat berisi penunjuk ke struktur; struktur, pada gilirannya, mungkin berisi string teks dan handel ke font logis yang akan digunakan aplikasi Anda untuk menggambar string.

Menu Owner-Drawn dan Pesan WM_MEASUREITEM

Sebelum sistem menampilkan item menu yang digambar pemilik untuk pertama kalinya, sistem mengirimkan pesan WM_MEASUREITEM ke prosedur jendela jendela yang memiliki menu item. Pesan ini berisi penunjuk ke struktur MEASUREITEMSTRUCT yang mengidentifikasi item dan berisi data item yang mungkin telah ditetapkan aplikasi ke dalamnya. Prosedur jendela harus mengisi itemWidth dan item Anggota struktur sebelum kembali dari memproses pesan. Sistem menggunakan informasi dalam anggota ini saat membuat persegi panjang pembatas di mana aplikasi menggambar item menu. Ini juga menggunakan informasi untuk mendeteksi kapan pengguna memilih item.

Menu Owner-Drawn dan Pesan WM_DRAWITEM

Setiap kali item harus digambar (misalnya, ketika pertama kali ditampilkan atau ketika pengguna memilihnya), sistem mengirim pesan WM_DRAWITEM ke prosedur jendela jendela jendela pemilik menu. Pesan ini berisi penunjuk ke struktur DRAWITEMSTRUCT , yang berisi informasi tentang item, termasuk data item yang mungkin telah ditetapkan aplikasi ke dalamnya. Selain itu, DRAWITEMSTRUCT berisi bendera yang menunjukkan status item (seperti apakah berwarna abu-abu atau dipilih) serta persegi panjang pembatas dan konteks perangkat yang digunakan aplikasi untuk menggambar item.

Aplikasi harus melakukan hal berikut saat memproses pesan WM_DRAWITEM :

  • Tentukan jenis gambar yang diperlukan. Untuk melakukannya, periksa anggota itemAction dari struktur DRAWITEMSTRUCT .
  • Gambar item menu dengan tepat, menggunakan persegi panjang pembatas dan konteks perangkat yang diperoleh dari struktur DRAWITEMSTRUCT . Aplikasi harus menggambar hanya dalam persegi panjang pembatas. Untuk alasan performa, sistem tidak mengklip bagian gambar yang digambar di luar persegi panjang.
  • Pulihkan semua objek GDI yang dipilih untuk konteks perangkat item menu.

Jika pengguna memilih item menu, sistem mengatur anggota itemAction dari struktur DRAWITEMSTRUCT ke nilai ODA_SELECT dan mengatur nilai ODS_SELECTED di anggota itemState . Ini adalah isu aplikasi untuk menggambar ulang item menu untuk menunjukkan bahwa item tersebut dipilih.

Menu Owner-Drawn dan Pesan WM_MENUCHAR

Menu selain menu yang digambar pemilik dapat menentukan menu mnemonic dengan menyisipkan garis bawah di samping karakter dalam string menu. Ini memungkinkan pengguna untuk memilih menu dengan menekan ALT dan menekan karakter mnemonic menu. Namun, dalam menu yang digambar pemilik, Anda tidak dapat menentukan mnemonik menu dengan cara ini. Sebagai gantinya, aplikasi Anda harus memproses pesan WM_MENUCHAR untuk menyediakan menu yang digambar pemilik dengan mnemonik menu.

Pesan WM_MENUCHAR dikirim ketika pengguna mengetikkan mnemonik menu yang tidak cocok dengan mnemonik yang telah ditentukan sebelumnya dari menu saat ini. Nilai yang terkandung dalam wParam menentukan karakter ASCII yang sesuai dengan kunci yang ditekan pengguna dengan tombol ALT. Kata wParam berurutan rendah menentukan jenis menu yang dipilih dan bisa menjadi salah satu nilai berikut:

  • MF_POPUP jika menu saat ini adalah submenu.
  • MF_SYSMENU jika menu adalah menu sistem.

Kata wParam berurutan tinggi berisi handel menu ke menu saat ini. Jendela dengan menu yang digambar pemilik dapat memproses WM_MENUCHAR sebagai berikut:

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

Keduanya dalam kata urutan tinggi dari nilai yang dikembalikan menginformasikan sistem bahwa kata berurutan rendah dari nilai yang dikembalikan berisi indeks berbasis nol dari item menu yang akan dipilih.

Konstanta berikut sesuai dengan kemungkinan nilai pengembalian dari pesan WM_MENUCHAR .

Terus-menerus Nilai Makna
MNC_IGNORE 0 Sistem harus membuang karakter yang ditekan pengguna dan membuat bip singkat pada speaker sistem.
MNC_CLOSE 1 Sistem harus menutup menu aktif.
MNC_EXECUTE 2 Sistem harus memilih item yang ditentukan dalam kata berurutan rendah dari nilai yang dikembalikan. Jendela pemilik menerima pesan WM_COMMAND .
MNC_SELECT 3 Sistem harus memilih item yang ditentukan dalam kata berurutan rendah dari nilai yang dikembalikan.

 

Mengatur Font untuk String Teks Menu-Item

Topik ini berisi contoh dari aplikasi yang menggunakan item menu yang digambar pemilik dalam menu. Menu berisi item yang mengatur atribut font saat ini, dan item ditampilkan menggunakan atribut font yang sesuai.

Berikut adalah bagaimana menu ditentukan dalam file definisi sumber daya. Perhatikan bahwa string untuk item menu Reguler, Tebal, Miring, dan Garis Bawah ditetapkan pada durasi, sehingga stringnya kosong dalam file definisi sumber daya.

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

Prosedur jendela aplikasi memproses pesan yang terlibat dalam menggunakan item menu yang digambar pemilik. Aplikasi ini menggunakan pesan WM_CREATE untuk melakukan hal berikut:

  • Atur bendera MF_OWNERDRAW untuk item menu.
  • Atur string teks untuk item menu.
  • Dapatkan handel font yang digunakan untuk menggambar item.
  • Dapatkan nilai warna teks dan latar belakang untuk item menu yang dipilih.

String teks dan handel font disimpan dalam array struktur MYITEM yang ditentukan aplikasi. Fungsi GetAFont yang ditentukan aplikasi membuat font yang sesuai dengan atribut font yang ditentukan dan mengembalikan handel ke font. Handel dihancurkan selama pemrosesan pesan WM_DESTROY .

Selama pemrosesan pesan WM_MEASUREITEM , contoh mendapatkan lebar dan tinggi string menu-item dan menyalin nilai-nilai ini ke dalam struktur MEASUREITEMSTRUCT . Sistem menggunakan nilai lebar dan tinggi untuk menghitung ukuran menu.

Selama pemrosesan pesan WM_DRAWITEM , string item menu digambar dengan ruang tersisa di samping string untuk bitmap tanda centang. Jika pengguna memilih item, teks dan warna latar belakang yang dipilih digunakan untuk menggambar item.

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

Contoh Item Menu Owner-Drawn

Contoh dalam topik ini menggunakan item menu yang digambar pemilik di menu. Item menu memilih atribut font tertentu, dan aplikasi menampilkan setiap item menu menggunakan font yang memiliki atribut yang sesuai. Misalnya, item menu Miring ditampilkan dalam font miring. Nama menu Karakter pada bilah menu membuka menu.

Bilah menu dan menu drop-down didefinisikan pada awalnya oleh sumber daya templat menu yang diperluas. Karena templat menu tidak dapat menentukan item yang digambar pemilik, menu awalnya berisi empat item menu teks dengan string berikut: "Reguler," "Tebal," "Miring," dan "Garis Bawah." Prosedur jendela aplikasi mengubahnya menjadi item yang digambar pemilik saat memproses pesan WM_CREATE . Ketika menerima pesan WM_CREATE , prosedur jendela memanggil fungsi OnCreate yang ditentukan aplikasi, yang melakukan langkah-langkah berikut untuk setiap item menu:

  • Mengalokasikan struktur MYITEM yang ditentukan aplikasi.
  • Mendapatkan teks item menu dan menyimpannya dalam struktur MYITEM yang ditentukan aplikasi.
  • Membuat font yang digunakan untuk menampilkan item menu dan menyimpan handelnya dalam struktur MYITEM yang ditentukan aplikasi.
  • Mengubah jenis item menu menjadi MFT_OWNERDRAW dan menyimpan penunjuk ke struktur MYITEM yang ditentukan aplikasi sebagai data item.

Karena penunjuk ke setiap struktur MYITEM yang ditentukan aplikasi disimpan sebagai data item, itu diteruskan ke prosedur jendela bersama dengan pesan WM_MEASUREITEM dan WM_DRAWITEM untuk item menu yang sesuai. Penunjuk terkandung dalam anggota itemData dari struktur MEASUREITEMSTRUCT dan DRAWITEMSTRUCT .

Pesan WM_MEASUREITEM dikirim untuk setiap item menu yang digambar pemilik saat pertama kali ditampilkan. Aplikasi memproses pesan ini dengan memilih font untuk item menu ke dalam konteks perangkat lalu menentukan ruang yang diperlukan untuk menampilkan teks item menu dalam font tersebut. Teks item font dan menu ditentukan oleh struktur item MYITEM menu (struktur yang ditentukan oleh aplikasi). Aplikasi menentukan ukuran teks dengan menggunakan fungsi GetTextExtentPoint32 .

Prosedur jendela memproses pesan WM_DRAWITEM dengan menampilkan teks item menu dalam font yang sesuai. Teks item font dan menu ditentukan oleh struktur item MYITEM menu. Aplikasi memilih warna teks dan latar belakang yang sesuai dengan status item menu.

Prosedur jendela memproses pesan WM_DESTROY untuk menghancurkan font dan memori kosong. Aplikasi menghapus font dan membebaskan struktur MYITEM yang ditentukan aplikasi untuk setiap item menu.

Berikut ini adalah bagian yang relevan dari file header aplikasi.

// 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 
 

Berikut ini adalah bagian yang relevan dari prosedur jendela aplikasi dan fungsi terkaitnya.

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

Menggunakan Bitmap Tanda Centang Kustom

Sistem menyediakan bitmap tanda centang default untuk ditampilkan di samping item menu yang dipilih. Anda dapat menyesuaikan item menu individual dengan menyediakan sepasang bitmap untuk mengganti bitmap tanda centang default. Sistem menampilkan satu bitmap ketika item dipilih dan yang lainnya ketika dihapus. Bagian ini menjelaskan langkah-langkah yang terlibat dalam membuat dan menggunakan bitmap tanda centang kustom.

Membuat Bitmap Tanda Centang Kustom

Bitmap tanda centang kustom harus berukuran sama dengan bitmap tanda centang default. Anda dapat mengambil ukuran tanda centang default bitmap dengan memanggil fungsi GetSystemMetrics . Kata berurutan rendah dari nilai pengembalian fungsi ini menentukan lebar; kata urutan tinggi menentukan tinggi.

Anda dapat menggunakan sumber daya bitmap untuk menyediakan bitmap tanda centang. Namun, karena ukuran bitmap yang diperlukan bervariasi tergantung pada jenis tampilan, Anda mungkin perlu mengubah ukuran bitmap pada waktu proses dengan menggunakan fungsi StretchBlt . Tergantung pada bitmap, distorsi yang disebabkan oleh ukuran dapat menghasilkan hasil yang tidak dapat diterima.

Alih-alih menggunakan sumber daya bitmap, Anda dapat membuat bitmap pada durasi dengan menggunakan fungsi GDI.

Untuk membuat bitmap pada durasi

  1. Gunakan fungsi CreateCompatibleDC untuk membuat konteks perangkat yang kompatibel dengan yang digunakan oleh jendela utama aplikasi.

    Parameter hdc fungsi dapat menentukan NULL atau nilai yang dikembalikan dari fungsi. CreateCompatibleDC mengembalikan handel ke konteks perangkat yang kompatibel.

  2. Gunakan fungsi CreateCompatibleBitmap untuk membuat bitmap yang kompatibel dengan jendela utama aplikasi.

    Parameter nWidth dan nHeight fungsi ini mengatur ukuran bitmap; mereka harus menentukan informasi lebar dan tinggi yang dikembalikan oleh fungsi GetSystemMetrics .

    Catatan

    Anda juga dapat menggunakan fungsi CreateBitmap untuk membuat bitmap monokrom.

     

  3. Gunakan fungsi SelectObject untuk memilih bitmap ke dalam konteks perangkat yang kompatibel.

  4. Gunakan fungsi gambar GDI, seperti Elips dan LineTo, untuk menggambar gambar ke bitmap, atau gunakan fungsi seperti BitBlt dan StretchBlt untuk menyalin gambar ke bitmap.

Untuk informasi selengkapnya, lihat Bitmap.

Mengaitkan Bitmap dengan Item Menu

Anda mengaitkan sepasang bitmap tanda centang dengan item menu dengan meneruskan handel bitmap ke fungsi SetMenuItemBitmaps . Parameter hBitmapUnchecked mengidentifikasi bitmap yang jelas, dan parameter hBitmapChecked mengidentifikasi bitmap yang dipilih. Jika Anda ingin menghapus satu atau kedua tanda centang dari item menu, Anda dapat mengatur parameter hBitmapUnchecked atau hBitmapChecked , atau keduanya, ke NULL.

Mengatur Atribut Tanda Centang

Fungsi CheckMenuItem mengatur atribut tanda centang item menu ke yang dipilih atau dikosongkan. Anda dapat menentukan nilai MF_CHECKED untuk mengatur atribut tanda centang ke dipilih dan nilai MF_UNCHECKED untuk mengaturnya agar dihapus.

Anda juga dapat mengatur status pemeriksaan item menu dengan menggunakan fungsi SetMenuItemInfo .

Terkadang sekelompok item menu mewakili sekumpulan opsi yang saling eksklusif. Dengan menggunakan fungsi CheckMenuRadioItem , Anda dapat memeriksa satu item menu sekaligus menghapus tanda centang dari semua item menu lainnya dalam grup.

Mensimulasikan Kotak Centang di Menu

Topik ini berisi contoh yang memperlihatkan cara mensimulasikan kotak centang di menu. Contoh berisi menu Karakter yang itemnya memungkinkan pengguna untuk mengatur atribut tebal, miring, dan garis bawah font saat ini. Saat atribut font berlaku, tanda centang ditampilkan dalam kotak centang di samping item menu yang sesuai; jika tidak, kotak centang kosong ditampilkan di samping item.

Contoh mengganti bitmap tanda centang default dengan dua bitmap: bitmap dengan kotak centang yang dipilih dan bitmap dengan kotak kosong. Bitmap kotak centang yang dipilih ditampilkan di samping item menu Tebal, Miring, atau Garis Bawah saat atribut tanda centang item diatur ke MF_CHECKED. Bitmap kotak centang bersih atau kosong ditampilkan saat atribut tanda centang diatur ke MF_UNCHECKED.

Sistem ini menyediakan bitmap yang telah ditentukan sebelumnya yang berisi gambar yang digunakan untuk kotak centang dan tombol radio. Contoh ini mengisolasi kotak centang yang dipilih dan kosong, menyalinnya ke dua bitmap terpisah, lalu menggunakannya sebagai bitmap yang dipilih dan dibersihkan untuk item di menu Karakter .

Untuk mengambil handel ke bitmap kotak centang yang ditentukan sistem, contohnya memanggil fungsi LoadBitmap , menentukan NULL sebagai parameter hInstance dan OBM_CHECKBOXES sebagai parameter lpBitmapName . Karena gambar dalam bitmap berukuran sama, contohnya dapat mengisolasinya dengan membagi lebar dan tinggi bitmap dengan jumlah gambar dalam baris dan kolomnya.

Bagian berikut dari file definisi sumber daya menunjukkan bagaimana item menu di menu Karakter ditentukan. Perhatikan bahwa awalnya tidak ada atribut font yang berlaku, sehingga atribut tanda centang untuk item Reguler diatur ke dipilih dan, secara default, atribut tanda centang dari item yang tersisa diatur ke kosong.

#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

Berikut adalah konten yang relevan dari file header aplikasi.

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

Contoh berikut menunjukkan bagian prosedur jendela yang membuat bitmap tanda centang; atur atribut tanda centang dari item menu Tebal, Miring, dan Garis Bawah ; dan hancurkan bitmap tanda centang.

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; 
    } 
} 

Contoh Penggunaan Bitmap Tanda Centang Kustom

Contoh dalam topik ini menetapkan bitmap tanda centang kustom ke item menu dalam dua menu. Item menu di menu pertama menentukan atribut karakter: tebal, miring, dan garis bawah. Setiap item menu dapat dipilih atau dibersihkan. Untuk item menu ini, contohnya menggunakan bitmap tanda centang yang menyerupai status kontrol kotak centang yang dipilih dan dihapus.

Item menu di menu kedua menentukan pengaturan perataan paragraf: kiri, tengah, dan kanan. Hanya salah satu item menu ini yang dipilih kapan saja. Untuk item menu ini, contohnya menggunakan bitmap tanda centang yang menyerupai status kontrol tombol radio yang dipilih dan dihapus.

Prosedur jendela memproses pesan WM_CREATE dengan memanggil fungsi OnCreate yang ditentukan aplikasi. OnCreate membuat empat bitmap tanda centang lalu menetapkannya ke item menu yang sesuai dengan menggunakan fungsi SetMenuItemBitmaps .

Untuk membuat setiap bitmap, OnCreate memanggil fungsi CreateMenuBitmaps yang ditentukan aplikasi, menentukan penunjuk ke fungsi gambar khusus bitmap. CreateMenuBitmaps membuat bitmap monokrom dari ukuran yang diperlukan, memilihnya ke dalam konteks perangkat memori, dan menghapus latar belakang. Kemudian memanggil fungsi gambar yang ditentukan untuk mengisi latar depan.

Empat fungsi gambar yang ditentukan aplikasi adalah DrawCheck, DrawUncheck, DrawRadioCheck, dan DrawRadioUncheck. Mereka menggambar persegi panjang dengan X, persegi panjang kosong, elips yang masing-masing berisi elips isian yang lebih kecil, dan elips kosong.

Prosedur jendela memproses pesan WM_DESTROY dengan menghapus bitmap tanda centang. Ini mengambil setiap handel bitmap dengan menggunakan fungsi GetMenuItemInfo dan kemudian meneruskan handel ke fungsi .

Saat pengguna memilih item menu, pesan WM_COMMAND dikirim ke jendela pemilik. Untuk item menu pada menu Karakter , prosedur jendela memanggil fungsi CheckCharacterItem yang ditentukan aplikasi. Untuk item pada menu Paragraf , prosedur jendela memanggil fungsi CheckParagraphItem yang ditentukan aplikasi.

Setiap item pada menu Karakter dapat dipilih dan dibersihkan secara independen. Oleh karena itu, CheckCharacterItem hanya mengalihkan status pemeriksaan item menu yang ditentukan. Pertama, fungsi memanggil fungsi GetMenuItemInfo untuk mendapatkan status item menu saat ini. Kemudian mengalihkan bendera status MFS_CHECKED dan mengatur status baru dengan memanggil fungsi SetMenuItemInfo .

Tidak seperti atribut karakter, hanya satu perataan paragraf yang dapat dipilih pada satu waktu. Oleh karena itu, CheckParagraphItem memeriksa item menu yang ditentukan dan menghapus tanda centang dari semua item lain pada menu. Untuk melakukannya, ini memanggil fungsi CheckMenuRadioItem .

Berikut ini adalah bagian yang relevan dari file header aplikasi.

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

Berikut ini adalah bagian yang relevan dari prosedur jendela aplikasi dan fungsi terkait.

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