深圳市龙华区观澜街道,梧州seo,医院网站建设 招标,js 捕获网站异常插件6.1 菜 单 设计 菜单是可视化编程的重要组成部分#xff0c;是一种方便地给命令分组并访问这些命令的方法。菜单通常用来显示程序的各项功能#xff0c;以方便用户选择执行#xff0c;通过对菜单命令进行编程可以调用程序中相应的功能。 6.1.1 菜单类CMenu 在MFC中#x… 6.1 菜 单 设计 菜单是可视化编程的重要组成部分是一种方便地给命令分组并访问这些命令的方法。菜单通常用来显示程序的各项功能以方便用户选择执行通过对菜单命令进行编程可以调用程序中相应的功能。 6.1.1 菜单类CMenu 在MFC中CMenu类封装了Windows的菜单功能它提供了多个方法用于创建、修改、合并菜单。CMenu类的主要方法如下。 1Attach方法该方法用于将句柄关联到菜单对象上。语法如下 BOOL Attach( HMENU hMenu ); 参数说明 hMenu标识菜单句柄。 返回值为非零表示执行成功否则执行失败。 2Detach方法该方法从菜单对象上分离菜单句柄。语法如下 HMENU Detach( ); 返回值分离的菜单句柄。 3FromHandle方法该方法根据菜单句柄返回一个菜单对象指针如果句柄没有关联的菜单对象则一个临时的菜单对象指针将要被创建。语法如下 static CMenu* PASCAL FromHandle( HMENU hMenu ); 参数说明 hMenu标识菜单句柄。 返回值菜单对象指针。 4CreateMenu方法该方法用于创建一个菜单窗口并将其关联到菜单对象上。语法如下 BOOL CreateMenu( ); 返回值执行成功返回值为非零否则为零。 5CreatePopupMenu方法该方法用于创建一个弹出式菜单窗口并将其关联到菜单对象上。语法如下 BOOL CreatePopupMenu( ); 返回值执行成功返回值为非零否则为零。对于弹出式菜单如果菜单窗口被释放菜单对象将要被自动释放。 6LoadMenu方法该方法从应用程序的可执行文件中加载一个菜单资源将其关联到菜单对象上。语法如下 BOOL LoadMenu( LPCTSTR lpszResourceName ); BOOL LoadMenu( UINT nIDResource ); 参数说明 lpszResourceName标识资源名称。 nIDResource标识资源ID。 返回值执行成功返回值为非零否则为零。 7DestroyMenu方法该方法用于释放菜单窗口当菜单窗口被释放前它将从菜单对象上分离出来。语法如下 BOOL DestroyMenu( ); 说明当菜单对象的析构函数被调用时将自动调用DestroyMenu方法释放菜单窗口。 8DeleteMenu方法该方法用于从菜单中删除一个菜单项。语法如下 BOOL DeleteMenu( UINT nPosition, UINT nFlags ); 参数说明 nPosition标识某一个菜单项。 nFlags表示如何删除菜单项可选值如下。 MF_BYCOMMAND根据nPosition标识的菜单ID删除菜单项。 MF_BYPOSITION根据nPosition标识的菜单位置删除菜单项。 9TrackPopupMenu方法该方法用于显示一个弹出式菜单。语法如下 BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect NULL ); 参数说明 nFlags表示屏幕位置标记和鼠标按钮标记。可选值如下。 TPM_CENTERALIGN在x水平位置居中显示菜单。 TPM_LEFTALIGN在x水平位置左方显示菜单。 TPM_RIGHTALIGN在x水平位置右方显示菜单。 TPM_LEFTBUTTON单击鼠标左键显示弹出式菜单。 TPM_RIGHTBUTTON 单击鼠标右键显示弹出式菜单。 x以屏幕坐标标识弹出式菜单的水平坐标。 y以屏幕坐标标识弹出式菜单的垂直坐标。 pWnd标识弹出式菜单的所有者。 lpRect以屏幕坐标表示用户在菜单中的单击区域如果为NULL当用户单击弹出式菜单之外的区域将释放菜单窗口。 10AppendMenu方法该方法用于在菜单项的末尾添加一个新菜单。语法如下 BOOL AppendMenu( UINT nFlags, UINT nIDNewItem 0, LPCTSTR lpszNewItem NULL ); BOOL AppendMenu( UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp ); 参数说明 nFlags标识菜单项的状态信息。 nIDNewItem标识菜单项的ID。 lpszNewItem标识菜单项的内容。 pBmp标识关联菜单项的位图对象指针。 11CheckMenuItem方法该方法用于在弹出的菜单项中放置或删除标记。语法如下 UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck ); 参数说明 nIDCheckItem指定由nCheck确定的将要检测的菜单项。 nCheck指定如何检测菜单项并如何决定菜单中子菜单的位置。可选值如下。 MF_BYCOMMAND指定参数给出已存在菜单项的命令ID号为默认值。 MF_BYPOSITION指定参数给出已存在菜单项的位置第一项位置为0。 MF_CHECKED与MF_UNCHECKED一起用作开关在菜单项之前放置默认的检测标记。 MF_UNCHECKED与MF_CHECKED一起用作开关删除菜单项之前的检测标记。 12CheckMenuRadioItem方法该方法用于将单选按钮放置在菜单项之前或从组中所有的其他菜单项中删除单选按钮。语法如下 BOOL CheckMenuRadioItem( UINT nIDFirst, UINT nIDLast, UINT nIDItem, UINT nFlags ); 参数说明 nIDFirst指定单选按钮组中的第一个菜单项的值。 nIDLast指定单选按钮组中的最后一个菜单项的值。 nIDItem指定单选按钮组中被选中的菜单项的值。 nFlags对nIDFirst、nIDLast和nIDItem的解释可选值如下 MF_BYCOMMAND指定参数给出已存在菜单项的命令ID号若没有设置MF_ BYCOMMAND和MF_BYPOSITION该值为默认值。 MF_BYPOSITION指定参数给出已存在菜单项的位置第一项位置为0。 13EnableMenuItem方法该方法用于设置菜单项有效、无效或变灰。语法如下 UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable ); 参数说明 nIDEnableItem指定nEnable决定的将要有效的菜单项该参数既可以指定弹出式菜单也可以指定标准菜单项。 nEnable指定了将要进行的动作可选值如下。 MF_BYCOMMAND指定参数给出已存在的菜单项的命令ID号此值为默认值。 MF_BYPOSITION指定参数给出已存在菜单项的位置第一项位置为0。 MF_DISABLED使菜单项无效但不变灰。 MF_ENABLED使菜单项有效。 MF_GRAYED使菜单项变灰。 14GetMenuItemCount方法该方法用于返回弹出式菜单或顶层菜单的菜单数。语法如下 UINT GetMenuItemCount( ) const; 返回值如果菜单项没有子菜单函数返回值为-1否则返回子菜单数。 15GetMenuItemID方法该方法根据菜单项的位置返回菜单ID如果菜单项是一个弹出式菜单返回值为-1如果菜单项是一个分隔条返回值为0。语法如下 UINT GetMenuItemID( int nPos ) const; 参数说明 nPos标识菜单项的位置。 16GetMenuState方法该方法用于返回指定菜单项的状态或弹出菜单的项数。语法如下 UINT GetMenuState( UINT nID, UINT nFlags ) const; 参数说明 nID指定由nFlags决定的菜单项ID。 nFlags指定nID的特性值为MF_BYCOMMAND和MF_BYPOSITION之一。 17GetMenuString方法该方法用于设置菜单项的文本。语法如下 int GetMenuString( UINT nIDItem, LPTSTR lpString, int nMaxCount, UINT nFlags ) const; int GetMenuString( UINT nIDItem, CString rString, UINT nFlags ) const; 参数说明 nIDItem标识菜单项位置或菜单项命令ID具体含义依赖于nFlags参数。 lpString标识一个字符缓冲区。 nMaxCount标识向字符缓冲区中复制的最大字符数。 rString标识一个字符串。 nFlags表示如何解释nIDItem。如果为MF_BYCOMMANDnIDItem标识菜单项命令ID如果为MF_BYPOSITIONnIDItem标识菜单项位置。 返回值实际复制到缓冲区中的字符数。 18GetSubMenu方法该方法用于获取弹出式菜单中的一个菜单项。语法如下 CMenu* GetSubMenu( int nPos ) const; 参数说明 nPos标识菜单项位置第一个菜单项对应的位置是0第二个菜单项对应的位置是1依次类推。 19InsertMenu方法该方法用于向菜单中指定位置插入菜单项。语法如下 BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem 0, LPCTSTR lpszNewItem NULL ); BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp ); 参数说明 nPosition标识某一个菜单项。 nFlags表示如何解释nPosition可选值如下 MF_BYCOMMAND根据nPosition标识的菜单ID插入菜单项。 MF_BYPOSITION根据nPosition标识的菜单位置插入菜单项。 nIDNewItem标识菜单项的ID。 lpszNewItem标识菜单项的内容。 pBmp标识关联菜单项的位图对象指针。 20ModifyMenu方法该方法用于修改菜单项信息。语法如下 BOOL ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem 0, LPCTSTR lpszNewItem NULL ); BOOL ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp ); 参数说明 nPosition标识某一个菜单项。 nFlags表示如何解释nPosition可选值如下。 MF_BYCOMMAND根据nPosition标识的菜单ID修改菜单项。 MF_BYPOSITION根据nPosition标识的菜单位置修改菜单项。 nIDNewItem标识菜单项的ID。 lpszNewItem标识菜单项的内容。 pBmp标识关联菜单项的位图对象指针。 21RemoveMenu方法该方法用于移除一个菜单项。语法如下 BOOL RemoveMenu( UINT nPosition, UINT nFlags ); 参数说明 nPosition标识某一个菜单项。 nFlags表示如何删除菜单项可选值如下 MF_BYCOMMAND根据nPosition标识的菜单ID删除菜单项。 MF_BYPOSITION根据nPosition标识的菜单位置删除菜单项。 22DrawItem方法该方法是一个虚方法用户可以改写该方法实现菜单的绘制。语法如下 virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ); 参数说明 lpDrawItemStruct是一个DRAWITEMSTRUCT结构指针该结构包含了菜单项的ID、类型、画布、句柄等详细信息。 6.1.2 菜单资源设计 下面来介绍如何设计菜单资源步骤如下。 1在工作区窗口中选择资源视图ResourceView鼠标右键单击一个节点在弹出的快捷菜单中选择“Insert”菜单项将打开“Insert Resource”对话框如图6.1所示。 图6.1 “Insert Resource”对话框 2在资源类型列表中选择“Menu”节点单击“New”按钮将创建一个菜单如图6.2所示。 图6.2 菜单设计窗口 3在菜单设计窗口中按Enter键打开“Menu Item Properties”窗口在“Caption”编辑框中设计菜单标题如图6.3所示。 图6.3 “Menu Item Properties”窗口 4关闭“Menu Item Properties”窗口返回到菜单设计窗口中如图6.4所示。 图6.4 菜单设计窗口 5在新建菜单下的虚线框上按Enter键打开“Menu Item Properties”窗口可以添加子菜单在“Menu Item Properties”窗口中设置子菜单ID和菜单的标题如图6.5所示。 图6.5 “Menu Item Properties”窗口 如果用户不设置菜单ID系统将会自动生成一个ID。 6关闭菜单属性窗口返回到菜单设计窗口中如图6.6所示。 说明如果要插入一个分隔线可以选择Separator属性并且菜单资源设计窗口支持鼠标拖曳功能可以在选中菜单项后将其拖曳至适当位置从而调整各个菜单的位置。 7如果想要设计一个级联菜单可以在菜单项的属性窗口中选中Pop-up复选框这样在菜单项的右侧将显示一个箭头效果如图6.7所示。 图6.6 菜单设计窗口 图6.7 菜单设计窗口 说明如果为菜单项选中Pop-up属性则该菜单项的ID将不能编辑。 8打开级联子菜单的属性窗口设置菜单ID和菜单标题。重复以上的步骤就可以设置需要的菜单。 说明在设计菜单项信息时可以为菜单项设置加速键来简化用户操作。在菜单标题的后面加“字母”即可实现加速键的设置程序运行时用户按下〈Alt〉键加上该字母键便可激活并操作该菜单。如果设置加速键的菜单项是子菜单还需要为该菜单的上级菜单设置加速键否则上级菜单没有加速键就不能运行子菜单的加速键。 6.1.3 菜单的显示与命令处理 在编辑了菜单资源之后运行程序并不能显示菜单要显示菜单还需要将菜单和对话框资源进行关联打开“Dialog Properties”窗口在“General”选项卡的“Menu”列表框中选择要进行关联的菜单ID如图6.8所示。 图6.8 “Dialog Properties”窗口 现在运行程序就包含菜单了。但是想要用菜单进行对程序的操作还需要设置菜单的命令处理。打开类向导选择“Message Maps”选项卡在“Class name”列表框选择关联菜单的对话框类在“Object Ids”列表中选择菜单项ID在“Messages”列表中选择“COMMAND”项如图6.9所示。 图6.9 类向导 单击“Add Function…”按钮弹出“Add Member Function”对话框并给出默认时的命令处理函数名如图6.10所示。 图6.10 “Add Member Function”对话框 单击“OK”按钮就添加了菜单项的命令处理函数。再单击“Edit Code”按钮可以打开添加的OnMenumessage命令处理函数在这里可以编辑菜单项的功能代码如下 void CMenuDlg::OnMenumessage() { MessageBox(菜单消息处理函数); return; } 6.1.4 动态创建菜单 在前面已经介绍了如何编辑菜单资源以及通过类向导编写菜单的命令处理函数。本节将通过CMenu类实现动态创建菜单。 1创建一个基于对话框的应用程序。 2向对话框中添加一个按钮控件。 3在主窗口的头文件中声明一个CMenu类对象代码如下 CMenu m_Menu; 4在资源头文件Resource.h中定义命令ID如图6.11所示。 图6.11 Resource.h文件 5在主窗口的头文件中添加消息处理函数如图6.12所示。 图6.12 DynamicMenuDlg.h文件 6在主窗口的源文件中添加消息映射宏将命令ID关联到消息处理函数中如图6.13所示。 7在主窗口的源文件中添加消息处理函数的实现代码代码如下 图6.13 DynamicMenuDlg.cpp文件 void CDynamicMenuDlg::OnMenured() { MessageBox(打开菜单被按下); } void CDynamicMenuDlg::OnMenublue() { MessageBox(保存菜单被按下); } void CDynamicMenuDlg::OnMenugreen() { MessageBox(另存为菜单被按下); } MessageBox函数用来弹出一个消息框该函数有3个参数第一个参数表示消息框显示的文本第二个参数表示消息框的标题默认为可执行文件名第3个参数表示消息框显示的按钮风格和图标风格的组合。 8处理“创建”按钮的消息响应事件用代码创建菜单代码如下 void CDynamicMenuDlg::OnButton1() { m_Menu.CreateMenu(); CMenu popmenu; popmenu.CreatePopupMenu(); m_Menu.AppendMenu(MF_POPUP,(UINT)popmenu.m_hMenu,系统); popmenu.AppendMenu(MF_STRING,ID_MENURED,打开); popmenu.AppendMenu(MF_STRING,ID_MENUBLUE,保存); popmenu.AppendMenu(MF_STRING,ID_MENUGREEN,另存为); popmenu.Detach(); SetMenu(m_Menu); } SetMenu函数用于分配一个菜单到指定窗口。 程序运行结果如图6.14所示。 图6.14 动态创建菜单 6.1.5 菜单项的更新机制 在使用类向导为菜单添加命令处理函数时发现菜单除了COMMAND消息外还有一个UPDATE_COMMAND_UI消息该消息是“更新命令用户接口消息”。菜单项的状态维护就依赖于UPDATE_COMMAND_UI消息。下面就来看看如何使用这个消息。 打开一个基于单文档的应用程序运行程序后发现“编辑”菜单下的菜单项都不可用如图6.15所示。 如果想要使“编辑”菜单下的菜单项都可用就要为相应的菜单项处理UPDATE_ COMMAND_UI消息。以“撤销”菜单项为例打开类向导选择“Message Maps”选项卡在“Class name”列表框中选择“CmainFrame”类在“Object Ids”列表中选择“撤销”菜单项“ID_EDIT_UNDO”在“Messages”列表中选择“UPDATE_COMMAND_UI”项单击“Add Function…”按钮创建该消息的处理函数如图6.16所示。 图6.16 类向导 单击“Edit Code”按钮定位到新建的消息处理函数在函数中添加代码使“撤销”菜单项可用代码如下 void CMainFrame::OnUpdateEditUndo(CCmdUI* pCmdUI) { pCmdUI-Enable(); //使菜单项可用 } 说明参数pCmdUI可以设置菜单项是否可用、是否具有标记和改变菜单项标题。 程序运行结果如图6.17所示。 图6.17 菜单项的更新机制 通过上面的运行结果发现“撤销”菜单项已经可用这是因为MFC菜单项的命令更新机制当要显示菜单时系统发出WM_INITMENUPOPUP消息该消息被程序窗口的基类接管后会创建一个CCmdUI对象CCmdUI对象会与程序的第一个菜单项相关联并调用CCmdUI对象的DoUpdate方法发送UPDATE_COMMAND_UI消息系统会查找是否有捕获这个消息的宏如果找到就调用相应的函数进行处理。当这个菜单项更新后CCmdUI对象会继续对下一个菜单项进行关联直到完成所有菜单项的处理。 6.1.6 自绘弹出式菜单 在Windows操作系统中单击鼠标右键弹出的菜单称为弹出式菜单。弹出式菜单可以方便用户的操作下面来介绍一下如何创建弹出式菜单。 1创建一个基于对话框的程序。 2向工程中导入两个位图将资源ID改为IDB_LEFTBITMAP和IDB_ITEMBITMAP。 3以CMenu类为基类派生一个CCustomMenu类定义一个记录菜单项信息的数据结构CMenuItemInfo代码如下 struct CMenuItemInfo { CString m_ItemText; //菜单项文本 int m_ItemIndex; //菜单项索引 int m_ItemID; //菜单标记0分隔条,其他普通菜单 }; 4在CCustomMenu类的头文件中声明如下变量 CMenuItemInfo m_ItemLists[10]; //菜单项信息 int m_ImageIndex; //图像索引 CFont m_TitleFont; //标题字体 5在CCustomMenu类中添加DrawMenuTitle方法用于绘制菜单左侧标题代码如下 void CCustomMenu::DrawMenuTitle(CDC *pDC, CRect rect, CString title) { CBitmap m_bitmap; m_bitmap.LoadBitmap(IDB_LEFTBITMAP); BITMAP m_size; m_bitmap.GetBitmap(m_size); CDC m_memdc; m_memdc.CreateCompatibleDC(pDC); CGdiObject* m_oldobject; m_oldobject m_memdc.SelectObject(m_bitmap); pDC-StretchBlt(0,0,28,rect.bottom,m_memdc,0,0,m_size.bmWidth,m_size.bmHeight,SRCCOPY); m_bitmap.DeleteObject(); CFont* m_oldfont pDC-SelectObject(m_TitleFont); pDC-TextOut(rect.left5,rect.Height()130,title); pDC-SelectObject(m_oldfont); } 通过LoadBitmap方法装载位图IDB_LEFTBITMAP再使用StretchBlt方法将位图绘制成标题大小。 6在CCustomMenu类中添加DrawItemText方法用于绘制菜单项文本代码如下 void CCustomMenu::DrawItemText(CDC *pDC, LPSTR str, CRect rect) { rect.DeflateRect(40,0,0,0); pDC-DrawText(str,rect,DT_SINGLELINE|DT_VCENTER|DT_LEFT); } 7在CCustomMenu类中添加DrawSeparater方法用于绘制分隔条代码如下 void CCustomMenu::DrawSeparater(CDC *pDC, CRect rect) { if(pDC ! NULL) { rect.DeflateRect(29,0,0,0); pDC-Draw3dRect(rect,RGB(0,0,255),RGB(0,0,255)); } } 8在CCustomMenu类中添加ChangeMenuItem方法用于修改菜单项信息代码如下 BOOL CCustomMenu::ChangeMenuItem(CMenu *menu) { int m_index0; if(menu ! NULL) { int m_itemcount menu-GetMenuItemCount(); //获得菜单项个数 for(int i0;im_itemcount;i) { //获得菜单项文本 menu-GetMenuString(i,m_ItemLists[m_index].m_ItemText,MF_BYPOSITION); int m_itemID menu-GetMenuItemID(i); //获得菜单项ID m_ItemLists[m_index].m_ItemID m_itemID; if(m_itemID0) { m_ItemLists[m_index].m_ItemIndex m_ImageIndex; m_ImageIndex; } //修改菜单项信息 menu-ModifyMenu(i,MF_OWNERDRAW | MF_BYPOSITION | MF_STRING, m_ItemLists[m_index].m_ItemID,(LPSTR)(m_ItemLists[m_index])); m_index; CMenu* m_subMenu menu-GetSubMenu(i); if(m_subMenu) { ChangeMenuItem(m_subMenu); } } } return TRUE; } 9改写CMenu类的MeasureItem方法设置菜单项大小代码如下 void CCustomMenu::MeasureItem(LPMEASUREITEMSTRUCT lpStruct) { if(lpStruct-CtlType ODT_MENU) { lpStruct-itemHeight 22; //设置菜单项的高 lpStruct-itemWidth 100; //设置菜单项的宽 CMenuItemInfo* m_iteminfo; m_iteminfo (CMenuItemInfo*)lpStruct-itemData; if(m_iteminfo-m_ItemID 0) { lpStruct-itemHeight 1; //设置分隔条 } } } 10改写CMenu类的DrawItem方法重绘菜单项代码如下 void CCustomMenu::DrawItem(LPDRAWITEMSTRUCT lpStruct) { if(lpStruct-CtlType ODT_MENU) { if(lpStruct-itemData NULL) return; unsigned int m_state lpStruct-itemState; //获得菜单项状态 CDC* pDC CDC::FromHandle(lpStruct-hDC); CString str ((CMenuItemInfo*)(lpStruct-itemData))-m_ItemText; LPSTR m_str str.GetBuffer(str.GetLength()); int m_itemID ((CMenuItemInfo*)(lpStruct-itemData))-m_ItemID; int m_itemicon ((CMenuItemInfo*)(lpStruct-itemData))-m_ItemIndex; CRect m_rect lpStruct-rcItem; pDC-SetBkMode(TRANSPARENT); //设置菜单项文本背景透明 switch(m_itemID) { case 0: //绘制分隔条 DrawSeparater(pDC,m_rect); break; default: //绘制菜单项和左侧标题 DrawComMenu(pDC,m_rect,m_stateODS_SELECTED); DrawItemText(pDC,m_str,m_rect); DrawMenuTitle(pDC,m_rect,明日科技有限公司); break; } } } 11在主窗口的头文件中声明一个CCustomMenu类对象m_Menu在主窗口的OnInit Dialog函数中添加如下代码装载菜单资源。 m_Menu.LoadMenu(IDR_MENU1); //装载菜单资源 m_Menu.ChangeMenuItem(m_Menu); 12处理主窗口的WM_CONTEXTMENU消息弹出菜单代码如下 void CPopMenuDlg::OnContextMenu(CWnd* pWnd, CPoint point) { CMenu* m_tempmenu m_Menu.GetSubMenu(0); m_tempmenu-TrackPopupMenu(TPM_LEFTBUTTON|TPM_LEFTALIGN,point.x,point.y,this); } 当鼠标右键单击程序窗口的客户区时程序会收到一条WM_CONTEXTMENU消息在该消息的处理函数中使用TrackPopupMenu方法弹出菜单。 13在主窗口中添加消息WM_DRAWITEM和WM_MEASUREITEM并分别在这两个消息的处理函数中调用m_Menu对象的DrawItem方法和MeasureItem方法。 菜单栏 在对话框窗口里显示菜单栏 像工具栏一样菜单栏在按件面板里没有对应的选项但有一个菜单控件类CMenu所以如果想要在对话框里显示菜单栏就得像工具栏那样到ResourceView选项卡里新建一个菜单栏资源步骤跟新建工具栏资源一样只是资源类型是Menu,菜单资源设计如下图 如果想改菜单项文本内容的话方法是右击要更改的菜单项选择属性接着会弹出这样一个对话框 上面那个ID项就是该菜单项对应的ID号了添加菜单项单击消息处理函数时会用到而标明项里的内容就是菜单项要显示的文本了。 这里还得注意一下“弹出”这个选项勾上这个选项表明对应的菜单项还有下级菜单如 上面“转到”这个菜单项具有弹出属性有下级菜单 设计好了菜单资源接着我们就来在对话框显示菜单栏吧方法是进入对话框编辑区右击对话框界面选择属性然后在菜单项里选择菜单资源ID号回车编译运行效果如下图 当然还有第二种在对话框显示菜单的方法调用SetMenu函数把菜单跟对话框关联起来函数第一个参数是窗口句柄第二个参数是菜单句柄。在OnInitDialog函数里添加如下语句 CMenu menu;//定义一个菜单类变量 menu.LoadMenu(IDR_MENU1);//装载IDR_MENU1菜单资源 SetMenu(menu);//和当前窗口关联起来 menu.Detach();//分离 如果要处理菜单项单击消息的话方法跟处理工具栏项单击消息一样进入类向导找到对应的菜单项ID为它添加COMMAND消息处理函数。 设置菜单左边显示位图和背景位图 CMenu类里要了解的函数 SetMenuItemBitmaps//设置菜单项左边的位图 函数定义BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked ); nPostion指明具体要设置的菜单项可以是菜单项索引菜单项ID具体由nFlags参数指明为MF_BYPOSITION则以菜单项索引指明 为MF_BYCOMMAND则第一个参数nPosition是菜单项ID号。pBmpUnchecked未被检测时显示的位图正常时pBmpChecked检测时显示的图片就是菜单项被打上勾时所显示的图片跟CheckMenuItem函数有关联 一个API函数SetMenuInfo,这个函数可以设置菜单重绘时选择的填充画刷类型该函数有两个参数第一个是要设置的菜单句柄第二个是一个MENUINFO结构指针我们只要了解这结构里有一个成员hbrBack就行了hbrBack是一个画刷句柄而菜单背景位图就通过创建位图画刷来实现的。 好了以上面的工程为例,引入三张位图ID号默认不变然后再引入一张位图菜单背景位图ID:IDB_MENUBACK),接着在对话框类的OnInitDialog函数里添加如下语句 CMenu *pMenuGetMenu();//获取对话框关联菜单 CMenu *pSubMenupMenu-GetSubMenu(0);//获得子菜单如果有0表示索引对应“文件”菜单 for(int i0;i3;i) { CBitmap bmp; bmp.LoadBitmap(IDB_BITMAP1i); pSubMenu-SetMenuItemBitmaps(i,MF_BYPOSITION,bmp,bmp); bmp.Detach(); } CBitmap bmp; CBrush m_BKBrush; bmp.LoadBitmap(IDB_MENUBACK); m_BKBrush.CreatePatternBrush(bmp);//创建位图画刷 MENUINFO mnInfo; memset(mnInfo,0,sizeof(MENUINFO)); mnInfo.cbSizesizeof(MENUINFO); mnInfo.fMaskMIM_BACKGROUND; mnInfo.hbrBackm_BKBrush; ::SetMenuInfo(pSubMenu-m_hMenu,mnInfo); m_BKBrush.Detach(); 运行效果如下图 PS不知道大家有没有碰到过这个问题,MENUINFO结构未定义解决的方法是进入文件选项卡FileView),在Source File文件下的StdAfx.cpp文件里的最前面部分添加这个语句#define WINVER 0x0501 设计弹出式菜单 CMenu类里要了解的函数 TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd,LPCRECT lpRect NULL ); 该函数用于在界面显示菜单nFlags参数指明菜单显示标志x,y用于确定菜单显示基于的坐标点pWnd是菜单相关联的窗口。 在“MFC类库详解”中有关参数nFlags的解释如下 指定屏幕位置标志或鼠标键标志。 屏幕位置标志可以为下列值之一 ·TPM_CENTERALIGN使弹出菜单在水平方向相对于x指定的坐标居中。·TPM_LEFTALIGN放置弹出菜单以便弹出菜单在由坐标值x指定的位置左对齐。·TPM_RIGHTALIGN放置弹出菜单以便弹出菜单在由坐标值x指定的位置右对齐。 鼠标键标志可以为下列值之一
·TPM_LEFTBUTTON导致弹出菜单追踪鼠标左键。·TPM_RIGHTBUTTON导致弹出菜单追踪鼠标右键。 以上面工程为例给对话框添加鼠标右键抬起WM_RBUTTONUP)消息处理函数在函数里添加如下代码 void CSeventhDlg::OnRButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CMenu *MenuGetMenu(); ClientToScreen(point);//将窗口坐标转换成屏幕坐标 Menu-GetSubMenu(0)-TrackPopupMenu( TPM_LEFTBUTTON|TPM_VERTICAL,point.x,point.y,this); Menu-Detach(); CDialog::OnRButtonUp(nFlags, point); } 要注意的是要在界面显示的菜单必须是一个弹出菜单虽然Menu-TrackPopupMenu也可以显示但显然不是想要的结果。 运行效果 动态纯代码创建一个菜单 上面的例子都是使用了菜单资源创建的菜单这一次我们用代码来创建菜单其实本质跟前面的用控件类的Create函数创建一个控件一样。只不过这里的“Create”函数是CreateMenu和CreatePopupMenu函数。 CMenu类里要了解的函数 CreateMenu //创建一个主菜单函数没有参数 CreatePopupMenu//创建一个具有弹出属性的菜单函数没有参数 AppendMenu//往一个菜单里添加菜单项或弹出式菜单 AppendMenu函数定义如下 BOOL AppendMenu( UINT nFlags, UINT nIDNewItem 0, LPCTSTR lpszNewItem NULL ); nFlags参数常用取值如下 MF_STRING 表明添加的是一个不具有弹出属性的菜单项。 MF_POPUP 添加的一个弹出菜单项 MF_SEPARATOR 添加的是一个菜单分隔条 MF_OWNERDRAW 表明对应菜单具有自绘属性 nIDNewItem参数如果添加的是一个不具有弹出属性的菜单项那么该值就是菜单项ID号否则是弹出式菜单句柄lpszNewItem是菜单项名称菜单文本内容 好了接着我们来动态创建一个菜单首先往对话框里添加一个按钮控件当单击这个按钮时就创建菜单响应这个按钮控件的单击消息消息处理函数里添加如下代码 CMenu Menu; Menu.CreateMenu();//创建一个主菜单 CMenu popMenu; popMenu.CreatePopupMenu();//创建一个弹出式菜单 popMenu.AppendMenu(MF_STRING,1001,新建);//添加菜单项 popMenu.AppendMenu(MF_STRING,1002,打开); popMenu.AppendMenu(MF_STRING,1003,保存); Menu.AppendMenu(MF_POPUP,(UINT)popMenu.m_hMenu,文件);//添加弹出菜单项 SetMenu(Menu);//关联到窗口中 Menu.Detach(); popMenu.Detach(); 更改菜单项大小宽高设置菜单文本字体大小 由于CMenu类里并没有提供设置菜单项大小以及字体大小的函数所以我们如果要实现上述功能的话只能采取自绘的方法。 如果对CMenu类的某些函数不了解的话可以参考MFC 类大全,这里就不讲述了 首先从CMenu派生出一个子类CNewMenu类的类型为Generic Class),然后往这个类添加三个成员函数MeasureItem设置菜单宽高, DrawItem自绘菜单ChangeMenuItem修改菜单项类型 三个函数分别定义如下 void CNewMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) void CNewMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) void CNewMenu::ChangeMenuItem(CMenu *pMenu) 其中MeasureItem和DrawItem是CMenu类的虚函数。 各函数的代码如下 void CNewMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { lpMeasureItemStruct-itemHeight25;//项高 lpMeasureItemStruct-itemWidth120;//项宽 } void CNewMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CRect rectlpDrawItemStruct-rcItem; CDC dc; dc.Attach(lpDrawItemStruct-hDC); dc.FillSolidRect(rect,RGB(0,166,170)); CFont Font; Font.CreatePointFont(125,宋体);//创建字体 dc.SelectObject(Font); CString *pText(CString *)lpDrawItemStruct-itemData; if(lpDrawItemStruct-itemStateODS_SELECTED) dc.FillSolidRect(rect,RGB(80,89,202));//菜单被选中 dc.SetTextColor(RGB(10,0,181));//设置文本颜色 dc.DrawText(*pText,rect,DT_VCENTER|DT_LEFT|DT_SINGLELINE); dc.Detach(); } void CNewMenu::ChangeMenuItem(CMenu *pMenu) { int itemCountpMenu-GetMenuItemCount(); for(int i0;iitemCount;i) { CString *pTextnew CString; UINT itemIDpMenu-GetMenuItemID(i);//获取菜单项ID号 pMenu-GetMenuString(i,*pText,MF_BYPOSITION);//获取菜单文本 //ModifyMenu函数最后一个参数对应DRAWITEMSTRUCT结构里的itemData变量 pMenu-ModifyMenu(i,MF_OWNERDRAW|MF_BYPOSITION|MF_STRING,itemID,(LPSTR)pText); if(itemID-1)//如果是一个弹出式菜单 { ChangeMenuItem(pMenu-GetSubMenu(i)); } } } 必须让每个菜单项具有MF_OWNERDRAW属性不然接不到WM_MEASUREITEM和WM_DRAWITEM消息而且菜单项不具有MF_OWNERDRAW属性 即使接到消息也无法自绘所以上面的ChangeMenuItem函数就是用于修改菜单项属性 WM_MEASUREITEM和WM_DRAWITEM消息不是直接发给菜单窗口的会被父窗口给收到所以得处理父窗口的WM_MEASUREITEM和WM_DRAWITEM消息给话框类添加这两个消息处理函数两个函数里的代码分别如下 void CFirstDlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) { // TODO: Add your message handler code here and/or call default if(lpMeasureItemStruct-CtlTypeODT_MENU)//如果类型是菜单 newMenu.MeasureItem(lpMeasureItemStruct);//调用CNewMenu类的MeasureItem成员函数 else CDialog::OnMeasureItem(nIDCtl, lpMeasureItemStruct); } void CFirstDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) { // TODO: Add your message handler code here and/or call default if(lpDrawItemStruct-CtlTypeODT_MENU) newMenu.DrawItem(lpDrawItemStruct); else CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct); } 接着我们在对话类添加一个成员变量 CNewMenu newMenu; 记得要包含头文件NewMenu.h),然后在对话框类的OnInitDialog函数添加如下代码 newMenu.LoadMenu(IDR_MENU1); SetMenu(newMenu); //只更改下主菜单下的第一项更改全部newMenu.ChangeMenuItem(newMenu); newMenu.ChangeMenuItem(newMenu.GetSubMenu(0)); IDR_MENU1是菜单资源的ID号可自行创建。好了到了这里大功已经告成了点编译运行效果如下 跟自绘按钮控件一样上面依然没处理菜单的所有状态如获得焦点被核记有无关联图片。也不处理菜单分隔条。。 如果想处理这些状态的话。建议查看DRAWITEMSTRUCT结构的itemState变量这个成员指明菜单项的状态 关联图片就查看CMenu类的函数。。。 菜单项数据(itemData)对应的分配内存就自己释放吧