Win32创建窗体菜单栏以及弹出菜单

⌚Time: 2023-01-15 19:34:48

👨‍💻Author: Jack Ge

创建顶级菜单CreateMenu

创建菜单。 菜单最初为空,但可以使用 InsertMenuItem、 AppendMenu 和 InsertMenu 函数填充菜单项。


HMENU CreateMenu();

将新项追加到指定菜单栏的末尾、下拉菜单、子菜单或快捷菜单。 可以使用此函数指定菜单项的内容、外观和行为。


BOOL AppendMenuA(

  [in]           HMENU    hMenu,

  [in]           UINT     uFlags,

  [in]           UINT_PTR uIDNewItem,

  [in, optional] LPCSTR   lpNewItem

);

[in] uFlags



类型: UINT



控制新菜单项的外观和行为。 此参数可以是以下值的组合。



Value   含义

MF_BITMAP

0x00000004L

使用位图作为菜单项。 lpNewItem 参数包含位图的句柄。

MF_CHECKED

0x00000008L

在菜单项旁边放置复选标记。 如果应用程序提供复选标记位图 (请参阅 SetMenuItemBitmaps,此标志会显示菜单项旁边的复选标记位图。

MF_DISABLED

0x00000002L

禁用菜单项,使其无法选中,但标志不会灰显。

MF_ENABLED

0x00000000L

启用菜单项,以便可以选择菜单项,并从其灰色状态还原它。

MF_GRAYED

0x00000001L

禁用菜单项并将其灰显,以便无法选择它。

MF_MENUBARBREAK

0x00000020L

与菜单栏 的MF_MENUBREAK 标志相同。 对于下拉菜单、子菜单或快捷菜单,新列与旧列之间将用一条竖线分隔。

MF_MENUBREAK

0x00000040L

将项放在菜单栏的新行 () 或 (的下拉菜单、子菜单或快捷菜单) 中,而不分隔列。

MF_OWNERDRAW

0x00000100L

指定该项是所有者绘制的项。 首次显示菜单之前,拥有菜单的窗口会收到 WM_MEASUREITEM 消息以检索菜单项的宽度和高度。 然后 ,只要 必须更新菜单项的外观,WM_DRAWITEM消息就会发送到所有者窗口的窗口过程。

MF_POPUP

0x00000010L

指定菜单项打开下拉菜单或子菜单。 uIDNewItem 参数指定下拉菜单或子菜单的句柄。 此标志用于向菜单栏添加菜单名称,或将子菜单打开到下拉菜单、子菜单或快捷菜单的菜单项。

MF_SEPARATOR

0x00000800L

绘制一条水平分割线。 此标志仅在下拉菜单、子菜单或快捷菜单中使用。 行不能灰显、禁用或突出显示。 忽略 lpNewItem 和 uIDNewItem 参数。

MF_STRING

0x00000000L

指定菜单项是文本字符串; lpNewItem 参数是指向字符串的指针。

MF_UNCHECKED

0x00000000L

不会在项旁边放置复选标记, (默认) 。 如果应用程序提供复选标记位图 (请参阅 SetMenuItemBitmaps) ,此标志会显示菜单项旁边的清除位图。

将新菜单分配给指定的窗口。


BOOL SetMenu(

  [in]           HWND  hWnd,

  [in, optional] HMENU hMenu

);

例子


HMENU hMenu = CreateMenu();

AppendMenu(hMenu, MF_STRING, 1000, "menu0");

AppendMenu(hMenu, MF_STRING, 1001, "menu1");

SetMenu(hwnd,hMenu);


创建弹出菜单

创建下拉菜单、子菜单或快捷菜单。 菜单最初为空。 可以使用 InsertMenuItem 函数插入或追加菜单项。 还可以使用 InsertMenu 函数插入菜单项和 AppendMenu 函数来追加菜单项。


HMENU CreatePopupMenu();

同样使用AppendMenu添加菜单项


BOOL AppendMenuA(

  [in]           HMENU    hMenu,

  [in]           UINT     uFlags,

  [in]           UINT_PTR uIDNewItem,

  [in, optional] LPCSTR   lpNewItem

);

代码


            hPopMenu = CreatePopupMenu();

            AppendMenu(hPopMenu, MF_STRING, 1010, "popmenu0");

            AppendMenu(hPopMenu, MF_STRING, 1011, "popmenu1");

弹出菜单

在指定位置显示快捷菜单,并跟踪菜单上的项目选择。 快捷菜单可在屏幕上的任意位置显示。


BOOL TrackPopupMenu(

  [in]           HMENU      hMenu,

  [in]           UINT       uFlags,

  [in]           int        x,

  [in]           int        y,

  [in]           int        nReserved,

  [in]           HWND       hWnd,

  [in, optional] const RECT *prcRect

);

参数

[in] hMenu



类型: HMENU



要显示的快捷菜单的句柄。 可以通过调用 CreatePopupMenu 来创建新的快捷菜单,或通过调用 GetSubMenu 检索与现有菜单项关联的子菜单的句柄来获取句柄。



[in] uFlags



类型: UINT



使用更多这些标志中的零来指定函数选项。



使用以下标志之一指定函数水平定位快捷菜单的方式。



Value   含义

TPM_CENTERALIGN

0x0004L

将快捷菜单水平相对于 x 参数指定的坐标居中。

TPM_LEFTALIGN

0x0000L

定位快捷菜单,使其左侧与 x 参数指定的坐标对齐。

TPM_RIGHTALIGN

0x0008L

定位快捷菜单,使其右侧与 x 参数指定的坐标对齐。

 

使用以下标志之一指定函数如何垂直定位快捷菜单。



Value   含义

TPM_BOTTOMALIGN

0x0020L

定位快捷菜单,使其底部与 y 参数指定的坐标对齐。

TPM_TOPALIGN

0x0000L

定位快捷菜单,使其顶部与 y 参数指定的坐标对齐。

TPM_VCENTERALIGN

0x0010L

将快捷菜单垂直相对于 y 参数指定的坐标居中。

 

使用以下标志控制用户选择的发现,而无需为菜单设置父窗口。



Value   含义

TPM_NONOTIFY

0x0080L

当用户单击菜单项时,该函数不会发送通知消息。

TPM_RETURNCMD

0x0100L

该函数在返回值中返回用户选择的菜单项标识符。

 

使用以下标志之一指定快捷菜单跟踪的鼠标按钮。



Value   含义

TPM_LEFTBUTTON

0x0000L

用户可以仅选择鼠标左键的菜单项。

TPM_RIGHTBUTTON

0x0002L

用户可以选择带有左右鼠标按钮的菜单项。

 

使用以下标志的任何合理组合来修改菜单的动画。 例如,通过选择水平标志和垂直标志,可以实现对角动画。



Value   含义

TPM_HORNEGANIMATION

0x0800L

从右到左对菜单进行动画处理。

TPM_HORPOSANIMATION

0x0400L

从左到右对菜单进行动画处理。

TPM_NOANIMATION

0x4000L

显示没有动画的菜单。

TPM_VERNEGANIMATION

0x2000L

从下到上对菜单进行动画处理。

TPM_VERPOSANIMATION

0x1000L

从上到下对菜单进行动画处理。

 

若要发生任何动画, SystemParametersInfo 函数必须设置 SPI_SETMENUANIMATION。 此外,如果菜单淡化动画处于打开状态,则忽略除 TPM_NOANIMATION之外的所有TPM_*ANIMATION 标志。 有关详细信息,请参阅 SystemParametersInfo 中的SPI_GETMENUFADE标志。



使用 TPM_RECURSE 标志显示另一个菜单时显示菜单。 这旨在支持菜单中的上下文菜单。



对于从右到左的文本布局,请使用 TPM_LAYOUTRTL。 默认情况下,文本布局从左到右。



[in] x



类型: int



快捷菜单的水平位置,以屏幕坐标为单位。



[in] y



类型: int



快捷菜单的垂直位置,以屏幕坐标为单位。



[in] nReserved



类型: int



保留;必须为零。



[in] hWnd



类型:HWND



拥有快捷菜单的窗口的句柄。 此窗口从菜单中接收所有消息。 在函数返回之前,该窗口不会从菜单中收到 WM_COMMAND 消息。 如果在 uFlags 参数中指定TPM_NONOTIFY,函数不会将消息发送到 hWnd 标识的窗口。 但是,仍必须在 hWnd 中传递窗口句柄。 它可以是应用程序的任何窗口句柄。



[in, optional] prcRect



类型: const RECT*



已忽略。

实现右键弹出菜单,只需要在窗体过程回调函数中对WM_RBUTTONUP(右键释放事件)消息进行处理,弹出菜单


switch(uMsg){



    case WM_RBUTTONUP:

        {

            POINT p;

            //lParam低位储存鼠标x位置

            p.x = LOWORD(lParam);

            //lParam高位储存鼠标y位置

            p.y = HIWORD(lParam);

            //客户端转换成屏幕坐标:

            ClientToScreen(hwnd,&p);

            //弹出菜单

            TrackPopupMenu(hPopMenu,TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);

        }

        break;

    ...

}

添加子菜单

使用AppendMenu函数可以实现子菜单的添加

AppendMenu(菜单句柄,MF_POPUP,(UINT)次级弹出菜单句柄,子菜单名称)

代码


            hPopMenu = CreatePopupMenu();

            AppendMenu(hPopMenu, MF_STRING, 1010, "popmenu0");

            AppendMenu(hPopMenu, MF_STRING, 1011, "popmenu1");

            //子菜单

            HMENU hPopMenuSubmenu = CreatePopupMenu();

            AppendMenu(hPopMenuSubmenu, MF_STRING, 10120, "submenu0");

            AppendMenu(hPopMenuSubmenu, MF_STRING, 10121, "submenu1");

            //添加子菜单

            AppendMenu(hPopMenu,MF_POPUP,(UINT)hPopMenuSubmenu,"popmenu2");

菜单点击处理

在窗体过程函数中,VM_COMMAND消息类型,通过判断wParam参数可以判断菜单ID并且进行处理




switch(uMsg){



    case WM_COMMAND:

        {

            switch(wParam){

            case 1000:

                MessageBox(NULL,"you clicked menu 0!","",MB_OK);

                break;

            case 1010:

                MessageBox(NULL,"you clicked POP menu 0!","",MB_OK);

                break;

            }

        }

        break;

    ...

}

完整代码


#include <windows.h>



LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);



int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow)

{

    // Register the window class.

    const char CLASS_NAME[]  = "testtClass";



    WNDCLASSA wc = { };



    wc.lpfnWndProc   = WindowProc;

    wc.hInstance     = hInstance;

    wc.lpszClassName = CLASS_NAME;



    RegisterClassA(&wc);



    // Create the window.



    HWND hwnd = CreateWindowExA(

        0,                              // Optional window styles.

        CLASS_NAME,                     // Window class

        "Test window",    // Window text

        WS_OVERLAPPEDWINDOW,            // Window style



        // Size and position

        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,



        NULL,       // Parent window    

        NULL,       // Menu

        hInstance,  // Instance handle

        NULL        // Additional application data

        );



    if (hwnd == NULL)

    {

        return 0;

    }



    ShowWindow(hwnd, nCmdShow);



    // Run the message loop.



    MSG msg = { };

    while (GetMessage(&msg, NULL, 0, 0) > 0)

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }



    return 0;

}

HMENU hMenu;

HMENU hPopMenu;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    switch (uMsg)

    {

    case WM_CREATE:

        {

            //建立顶级菜单

            hMenu = CreateMenu();

            AppendMenu(hMenu, MF_STRING, 1000, "menu0");

            AppendMenu(hMenu, MF_STRING, 1001, "menu1");

            //设置窗体菜单

            SetMenu(hwnd,hMenu);

            //建立子菜单

            HMENU hMenuSubmenu = CreatePopupMenu();

            AppendMenu(hMenuSubmenu, MF_STRING, 10020, "submenu0");

            AppendMenu(hMenuSubmenu, MF_STRING, 10021, "submenu1");

            //添加子菜单到顶级菜单

            AppendMenu(hMenu,MF_POPUP,(UINT)hMenuSubmenu,"menu2");





            //建立弹出菜单

            hPopMenu = CreatePopupMenu();

            AppendMenu(hPopMenu, MF_STRING, 1010, "popmenu0");

            AppendMenu(hPopMenu, MF_STRING, 1011, "popmenu1");

            //建立子菜单

            HMENU hPopMenuSubmenu = CreatePopupMenu();

            AppendMenu(hPopMenuSubmenu, MF_STRING, 10120, "submenu0");

            AppendMenu(hPopMenuSubmenu, MF_STRING, 10121, "submenu1");

            //添加子菜单到弹出菜单

            AppendMenu(hPopMenu,MF_POPUP,(UINT)hPopMenuSubmenu,"popmenu2");

        }

        break;

    case WM_RBUTTONUP://鼠标右键事件

        {

            POINT p;

            //lParam低位储存鼠标x位置

            p.x = LOWORD(lParam);

            //lParam高位储存鼠标y位置

            p.y = HIWORD(lParam);

            //客户端转换成屏幕坐标:

            ClientToScreen(hwnd,&p);

            //弹出菜单

            TrackPopupMenu(hPopMenu,TPM_RIGHTBUTTON,p.x,p.y,0,hwnd,NULL);

        }

        break;

    case WM_COMMAND:

        {

            switch(wParam){

            case 1000://点击了顶级菜单0

                MessageBox(NULL,"you clicked menu 0!","",MB_OK);

                break;

            case 1010://点击了弹出菜单0

                MessageBox(NULL,"you clicked POP menu 0!","",MB_OK);

                break;

            }

        }

        break;

    case WM_DESTROY:

        DestroyMenu(hPopMenu);

        PostQuitMessage(0);

        return 0;



    case WM_PAINT:

        {

            PAINTSTRUCT ps;

            HDC hdc = BeginPaint(hwnd, &ps);



            // All painting occurs here, between BeginPaint and EndPaint.



            FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));



            EndPaint(hwnd, &ps);

        }

        return 0;



    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);

}




销毁菜单


函数原型:BOOL DestroyMenu(HMENU hMenu);

参数:

hMenu:要销毁的菜单的句柄。

返回值:如果函数调用成功,返回非零值;如果函数调用失败,返回值是零。若想获得更多的错误信息,请调用GetLastError函数。

备注:一个应用程序在关闭之前,必须调用函数DestroyMenu来销毁一个没被分配给窗口的菜单。分配给窗口的菜单,当应用程序关闭时,被自动销毁。