Menu Basic

1. 菜单是Windows的标准界面元素,几乎所有的Windows应用程序中都有它的身影。MFC中的类CMenu对它进行了封装,使其使用起来更加简便。 要在程序中使用菜单也很简单,一般分以下几步:第一步先用VC的菜单编辑器创建一个菜单资源,给它赋予我们自己定义的ID,第二步在程序中构造一个 CMenu类的对象,用CMenu::LoadMenu函数将菜单从资源中装入,接着调用CWnd::SetMenu函数将新菜单连到相应的窗口中,最后 切记,要调用Detach成员函数把CMenu对象和菜单分离,以确保当CMenu对象生存期结束时,菜单不会被销毁。 

Create a CMenu object on the stack frame as a local, then call CMenu’s member functions to manipulate the new menu as needed. Next, call CWnd::SetMenu to set the menu to a window, followed immediately by a call to the CMenu object’s Detach member function. The CWnd::SetMenu member function sets the window’s menu to the new menu, causes the window to be redrawn to reflect the menu change, and also passes ownership of the menu to the window. The call to Detach detaches the HMENU from the CMenu object, so that when the local CMenu variable passes out of scope, the CMenu object destructor does not attempt to destroy a menu it no longer owns. The menu itself is automatically destroyed when the window is destroyed.

2. MFC中菜单项消息如果利用ClassWizard来对菜单项消息分别在上述四个类中进行响应,则菜单消息传递顺序:View类--Doc类--CMainFrame类--App类。菜单消息一旦在其中一个类中响应则不再在其它类中查找响应函数。
具体:

当点击一个菜单项的时候,最先接受到菜单项消息的是CMainFrame框架类,CMainFrame框架类将会把菜单项消息交给它的子窗口View类,由 View类首先进行处理;如果View类检测到没对该菜单项消息做响应,则View类把菜单项消息交由文档类Doc类进行处理;如果Doc类检测到Doc 类中也没对该菜单项消息做响应,则Doc类又把该菜单项消息交还给View类,由View类再交还给CMainFrame类处理。如果 CMainFrame类查看到CMainFrame类中也没对该消息做响应,则最终交给App类进行处理。

3. Menu Item VS Popup Menu.

Menu和Pop-up   Menu,是有区别的。普通菜单(就是用CreateMenu函数创建出来的菜单),直观地说,就是固定在标题栏下方的菜单,每Append一个菜单项,横向上就多出一列出来。让我们在OnInitDialog函数中添加如下代码:  
  CMenu Menu;  
  Menu.CreateMenu();  
  SetMenu(&Menu);  
  Menu.Detach();  
  编译运行,没有效果,这是显而易见的,因为这时Menu还是空的,因为我们没有添加任何菜单项。截图如下:  
     
  好,现在让我们向菜单中添加一个菜单项。在Menu.CreateMenu()语句下面插入下面的代码:  
  Menu.AppendMenu(MF_STRING,   ID_APP_FILE,   "&File");  
  好了,有效果了,出来一个菜单,但是鼠标按下去什么都没有,仔细想想也很合理,只是添加了一个菜单项而已,一个菜单项怎么会弹出另一个菜单呢?效果如下(可以看到鼠标按下去什么都没有出来)  
     
  还记得刚才我说的吗,普通菜单其实就是横向的那一条,每一个都是一个菜单项。好,现在让我们来试验一下,再在Menu中添加一个菜单项:  
  Menu.AppendMenu(MF_STRING,   ID_APP_EDIT,   "&Edit");  
  可以看到又出来一项,仍然是按下去什么都没有,但这证明了我们之前的结论——Menu就好比横过来的一个菜单,有固定的位置。  
     
  如何要让这个菜单项按下去出现另一个菜单,就要另外创建一个浮动菜单(Pop-up   Menu),因为只有浮动菜单可以连到另一个菜单中去。好,让我们修改代码成下面这个样子:  
  CMenu Menu,PopupMenu;  
   
  Menu.CreateMenu();  
  PopupMenu.CreatePopupMenu();  
   
  PopupMenu.AppendMenu(MF_STRING,IDC_POPMENU_FILE_NEW,"&New");  
  PopupMenu.AppendMenu(MF_STRING,IDC_POPMENU_FILE_OPEN,"&Open");  
  PopupMenu.AppendMenu(MF_STRING,IDC_POPMENU_FILE_CLOSE,"&Close");  
  PopupMenu.AppendMenu(MF_STRING,IDC_POPMENU_FILE_EXIT,"E&xit");  
   
  Menu.AppendMenu(MF_POPUP,   (UINT)PopupMenu.m_hMenu,   "&File");   //关键!  
  Menu.AppendMenu(MF_STRING,   ID_APP_EDIT,   "&Edit");  
   
  SetMenu(&Menu);  
  Menu.Detach();  
  PopupMenu.Detach();  
  然后看看效果:  
     
  现在我们来详细分析一下上面的代码。  
  首先,再创建一个CMenu类的对象PopupMenu,然后调用函数CreatePopupMenu创建了一个空的浮动菜单。紧接着向这个空的浮动菜单中加入我们想要的菜单项。接着是最关键的一步:  
  Menu.AppendMenu(MF_POPUP,   (UINT)PopupMenu.m_hMenu,   "&File");  
  这一步是向普通菜单Menu中追加一项。注意这里的参数和下一句有什么不同。第一个参数设成MF_POPUP,表示本菜单项(即“File”这个菜单 项),是用来操纵一个浮动菜单的,第二个参数,原本应该是新菜单项的命令ID,这里设成子菜单的句柄。因为单击Fiile菜单项的时候,它的动作是弹出浮 动菜单,所以它就没有必要拥有命令ID了(命令ID只是为了处理单击消息而设),最后一个参数,是这个菜单项在Menu中的标题,如果换成别的字符串,譬 如Hello,那么最上面的File将变成Hello。  

  这里要强调一点,普通的Menu(相对Pop-up   Menu而言),就只是标题栏下面那一条,这一行上都是它的菜单项(你可以把它看成是一个横过来放的菜单);而所有在鼠标点击后才出现的菜单(不管是左键 还是右键),统统都是Pop-up   Menu(也可以认为,Menu就是横排的菜单,Pop-up   Menu就是竖排的菜单)。以一个Windows   Shell窗口为例,只有最上一行才是Menu,其中诸如“文件”,“编辑”等等,每个都是Menu的一个Item,其余,都是Pop-up   Menu。 

4. About Popup Menu

1) 两个重要的函数

--CWnd::OnContextMenu Called by the framework when the user has clicked the right mouse button (right clicked) in the window. You can process this message by displaying a context menu using the TrackPopupMenu.
--BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );

//CMenu::TrackPopupMenu Displays a floating pop-up menu at the specified location and tracks the selection of items on the pop-up menu. A floating pop-up menu can appear anywhere on the screen.

2) 利用调用TrackPopupMenu函数,手工添加弹出菜单:
1)用资源管理器添加一个菜单资源
2)在鼠标右键消息响应函数中,加载菜单资源,并获得要显示的子菜单指针,并用该指针调用TrackPopupMenu函数便完成任务(但要注意:鼠标响应函 数传进来的坐标是客户区坐标,而TrackPopupMenu函数中使用的是屏幕坐标,在调用TrackPopupMenu前要调用 ClientToScreen客户区坐标到屏幕坐标的转换)
示例代码:
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
ClientToScreen(&point);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,this);
说明:
CWnd::ClientToScreen(..);//将一个坐标点或一个矩形区域坐标转换成屏幕坐标。
CMenu::TrackPopupMenu(..);//在指定位置以指定的方式显示弹出菜单。
CWnd::ScreenToClient(..);
//Converts the screen coordinates of a given point or rectangle on the display to client coordinates.

5. 相关重要函数:

CMenu* GetMenu( ) ;//CWnd::GetMenu得到窗口菜单栏对象指针。
CMenu* GetSubMenu(  ) ;//CMenu::GetSubMenu获得指向弹出菜单对象指针
UINT CheckMenuItem( );//CMenu::CheckMenuItem Adds check marks to or removes check marks from menu items in the pop-up menu.
BOOL SetDefaultItem();//CMenu::SetDefaultItem Sets the default menu item for the specified menu.
BOOL SetMenuItemBitmaps( );//CMenu::SetMenuItemBitmaps 设置位图标题菜单。
UINT EnableMenuItem();//CMenu::EnableMenuItem使菜单项有效,无效,或变灰。
BOOL SetMenu( CMenu* pMenu );//CWnd::SetMenu在当前窗口上设置新菜单或移除菜单。
HMENU Detach( );//CMenu::Detach Detaches a Windows menu from a CMenu object and returns the handle.


posted @ 2009-05-11 23:01  能巴  阅读(361)  评论(0编辑  收藏  举报