理论
创建普通菜单步骤如下:
- 1)创建菜单 如:CreateMenu
- 2) 添加菜单项 如:AppendMenu
- 3)加载菜单 LoadMenu
- 4)设置菜单到窗口 SetMenu
- 5)处理菜单响应
- 6)消除菜单 DestroyMenu
创建右键菜单的步骤如下:
- 1)创建菜单 如:CreateMenu
- 2) 添加菜单项 如:AppendMenu
- 3)使用TrackPopupMenu加载菜单。
- 4)处理菜单响应
- 5)消除菜单 DestroyMenu
当然,如果你选择在已有菜单上进行编辑的话,也是可以的,就省略了其中的一些步骤。本节的例子就是在已有的菜单里面添加自己定义的菜单项。对于右键菜单你也可以选择已有的菜单中的一栏作为弹出菜单。 使用菜单后,要涉及清除的问题,和窗口相连的菜单句柄在窗口摧毁的时候会由Windows自动释放,不需要手工释放,但没有和窗口相连的菜单就要由程序自己来释放了,方法是使用DestroyMenu函数,比如没有和窗口相连而仅用TrackPopupMenu弹出的菜单句柄。本例中,我们使用的是窗口关联的菜单句柄,因此我们不需要调用DestroyMenu消除菜单资源。
菜单消息:当用户选择一个菜单项时,windwos通常向窗口过程发送几个不同的消息。在大多数情况下,你的程序可以忽略大部分的消息,只需要把他们传递给DefWindowProc即可。
- WM_INITMENU 消息,wParam :主菜单句柄, lParam : 0;这个消息是当一个菜单将要激活时发出。当用户点击菜单栏上的项或者 使用菜单键选择 。这个消息允许应用程序在显示之前修改菜单。可以在这时修改顶层菜单。
- WM_MENUSELECT: LOWORD(wParam): 选中项:菜单id或者弹出式菜单句柄。HIWORD(wParam): 选择标; lParam: 包含选中项的菜单句柄。当用户选择某个菜单项时,这个消息就会被发送到菜单所属的窗口。 这个消息是一个菜单跟踪消息,wParam告诉你当前选中的是菜单中的哪一项,wParam的高位字中的“选择标志”可以是下列这些标志的组合: MF_GRAYED、MF_DISABLED、MF_CHECKED、MF_BITMAP、MF_POPUP、MF_HELP、MF_SYSMENU和 MF_MOUSESELECT. 如果你需要根据菜单项的选择来改变窗口客户区的内容,那么可以你可以使用这个消息。
- WM_INITMENUPOPUP: wParam: 弹出式菜单句柄; LOWORD(lParam):弹出式菜单索引; HIWORD(lParam):系统菜单为1,其他为0 ; 如果要在弹出式菜单显示之前启用或者禁用菜单项,那么这个消息就很重要。
- WM_COMMAND: LOWORD(wParam): 菜单id; HIWOED(wParam): 0 ; lParam: 0 ; 如果这个消息是由子窗口控制产生,如button产生则:LOWORD(wParam): 控制ID HIWORD(lParam): 通知码 lParam: 子窗口句柄。如果这个消息是由子窗口或者快捷键产生,则通知码为1,由菜单产生,通知码为0。通过参数,可以区分这个消息的来源是来自于控件,快捷键还是菜单。
- WM_SYSCOMMAND: 类似于WM_COMMAND消息,只是WM_SYSCOMMAND表示用户从系统菜单中选择一个启动菜单项或者用户选择最大化,最小化,关闭按钮。 LOWORD(wParam): 菜单id HIWOED(wParam): 0 lParam: 0;如果消息是由按鼠标产生的,LOWORD(wParam)和HIWORD(lParam)将包含鼠标光标的位置x,y屏幕坐标。对于windows预先定义的系统菜单项,用wParam表示。 SC_CLOSE,SC_MAXIMIZE,SC_MINIMIZE,SC_MOVE等等。
对于已有菜单,我们可以使用GetSystemMenu,GetMenu ,GetSubMenu来获取菜单句柄。当需要更改菜单时,可以使用下面的API进行更改。 AppendMenu, DeleteMenu,InsertMenu,ModifyMenu,RemoveMenu等等。
实例
资源:菜单和快捷键
///////////////////////////////////////////////////////////////////////////// // // Menu IDM_MAIN MENU BEGIN POPUP "文件(&F)" BEGIN MENUITEM "打开文件(&O)...", IDM_OPEN MENUITEM "关闭文件(&C)...", IDM_OPTION MENUITEM SEPARATOR MENUITEM "退出(&X)", IDM_EXIT END POPUP "查看(&V)" BEGIN MENUITEM "字体(&F)... Alt+F", IDM_SETFONT MENUITEM "背景色(&B)... Ctrl+Alt+B", IDM_SETCOLOR MENUITEM SEPARATOR MENUITEM "被禁用的菜单项", IDM_INACT, INACTIVE MENUITEM "被灰化的菜单项", IDM_GRAY, GRAYED MENUITEM SEPARATOR MENUITEM "大图标(&G)", IDM_BIG MENUITEM "小图标(&M)", IDM_SMALL MENUITEM "列表(&L)", IDM_LIST MENUITEM "详细资料(&D)", IDM_DETAIL MENUITEM SEPARATOR POPUP "工具栏(&T)" BEGIN MENUITEM "标准按钮(&S)", IDM_TOOLBAR MENUITEM "文字标签(&C)", IDM_TOOLBARTEXT MENUITEM "命令栏(&I)", IDM_INPUTBAR END MENUITEM "状态栏(&U)", IDM_STATUSBAR END POPUP "帮助(&H)" BEGIN MENUITEM "帮助主题(&H) F1", IDM_HELP MENUITEM SEPARATOR MENUITEM "关于(&A) ...", IDM_ABOUT END END ///////////////////////////////////////////////////////////////////////////// // // Accelerator IDC_SDKPOPUPMENU ACCELERATORS BEGIN "/", IDM_ABOUT, ASCII, ALT, NOINVERT "?", IDM_ABOUT, ASCII, ALT, NOINVERT VK_F1, IDM_HELP, VIRTKEY, NOINVERT "B", IDM_SETCOLOR, VIRTKEY, CONTROL, ALT, NOINVERT "F", IDM_SETFONT, VIRTKEY, ALT, NOINVERT END关键代码
resouces.h
#define IDC_MYICON 2 #define IDD_SDKPOPUPMENU_DIALOG 102 #define IDS_APP_TITLE 103 #define IDD_ABOUTBOX 103 #define IDI_SDKPOPUPMENU 107 #define IDI_SMALL 108 #define IDC_SDKPOPUPMENU 109 #define IDR_MAINFRAME 128 #define IDM_MAIN 0x2000 #define IDA_MAIN 0x2000 #define IDM_OPEN 0x4101 #define IDM_OPTION 0x4102 #define IDM_EXIT 0x4103 #define IDM_SETFONT 0x4201 #define IDM_SETCOLOR 0x4202 #define IDM_INACT 0x4203 #define IDM_GRAY 0x4204 #define IDM_BIG 0x4205 #define IDM_SMALL 0x4206 #define IDM_LIST 0x4207 #define IDM_DETAIL 0x4208 #define IDM_TOOLBAR 0x4209 #define IDM_TOOLBARTEXT 0x4210 #define IDM_INPUTBAR 0x4211 #define IDM_STATUSBAR 0x4212 #define IDM_HELP 0x4301 #define IDM_ABOUT 0x4302 TCHAR szClassName[] = L"Menu Example"; TCHAR szCaptionMain[] = L"menu"; TCHAR szMenuHelp[] = L"帮助主题(&H)"; TCHAR szMenuAbout[] = L"关于本程序(&A)..."; HWND g_hWinMain; HMENU g_hMenu; HMENU g_hSubMenu; void DisplayMenuItem(DWORD dwCommandID) { TCHAR szBuffer[256] = {0}; wsprintf(szBuffer, L"您选择了菜单命令:%08x",dwCommandID); MessageBox(g_hWinMain,szBuffer, L"菜单选择",MB_OK); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // 将实例句柄存储在全局变量中 g_hMenu = LoadMenu(hInstance,MAKEINTRESOURCE(IDM_MAIN));//加载菜单资源,返回菜单句柄 g_hSubMenu = GetSubMenu(g_hMenu,1); //获取主菜单的第2个子菜单,用作快捷菜单 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, g_hMenu, hInstance, NULL);//把菜单放到窗口中 g_hWinMain=hWnd; if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; POINT stPos; HMENU hSysMenu; switch (message) { case WM_CREATE: hSysMenu = GetSystemMenu(hWnd,FALSE);//获取系统菜单句柄 AppendMenu(hSysMenu,MF_SEPARATOR,0,NULL);//把分割线附加到系统菜单中 AppendMenu(hSysMenu,0,IDM_HELP,szMenuHelp);//把菜单项help附加到系统菜单中 AppendMenu(hSysMenu,0,IDM_ABOUT,szMenuAbout);//把菜单项about附加到系统菜单中 break; case WM_COMMAND: //用户菜单项命令的消息 DisplayMenuItem(wParam);//显示对话框 //对于单项项的标识(单项,双选)的标识 if(LOWORD(wParam) == IDM_EXIT) { DestroyWindow(hWnd); } else if(LOWORD(wParam) >= IDM_TOOLBAR && LOWORD(wParam) <= IDM_STATUSBAR) //多选的 工具栏的:标准按钮,命令栏,文字标签 { UINT uState = GetMenuState(g_hMenu,LOWORD(wParam),MF_BYCOMMAND); if(uState == MF_CHECKED) uState = MF_UNCHECKED; else uState = MF_CHECKED; CheckMenuItem(g_hMenu,LOWORD(wParam),uState);//复选或撤销指定菜单的菜单条目 } else if(LOWORD(wParam) >= IDM_BIG && LOWORD(wParam) <= IDM_DETAIL)//单选的 大图标,小图标,列表,详细资料 { CheckMenuRadioItem(g_hMenu,IDM_BIG,IDM_DETAIL,LOWORD(wParam),MF_BYCOMMAND);//单选[IDM_BIG,IDM_DETAIL]中的一个 } //CheckMenuItem,CheckMenuRadioItem分别用于改变菜单项的状态。菜单项的状态可以通过GetMenuState函数获得。 break; case WM_SYSCOMMAND://系统菜单项的命令消息 if(LOWORD(wParam) == IDM_HELP || LOWORD(wParam) == IDM_ABOUT)//附加到系统菜单的help,about的菜单项 DisplayMenuItem(wParam); else return DefWindowProc(hWnd,message,wParam,lParam); break; case WM_RBUTTONDOWN: GetCursorPos(&stPos);//获取光标位置 TrackPopupMenu(g_hSubMenu,TPM_LEFTALIGN,stPos.x,stPos.y,NULL,hWnd,NULL);//在指定位置显示快捷菜单,并跟踪菜单选项 break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }