菜单使用简介

    一个菜单拦可以有若干个子菜单,一个子菜单又可以有若干个菜单项等。对菜单栏的子菜单由左至右建立从0开始的索引。对特定子菜单的菜单项由上至下建立了从0开始的索引。访问子菜单和菜单项均可以通过其索引或标识(如果有标识的话)进行。


菜单只有一个! 子菜单   !   菜单项 !索引都是从0开始


//菜单的操作是在 框架类(Frame) 进行的 :

定位菜单的方法及有关函数:

第一步获得菜单:
CWnd::CMenu* GetMenu( ) const; //指针

第二步获得子菜单:
CMenu::CMenu* GetSubMenu( int nPos ) const;
参数 nPos 是子菜单的位置(索引)从0开始;

第三步对菜单项操作~

可以一起使用   GetMenu()->GetSubMenu(0)->?


菜单的特殊操作

1 给菜单前打对号 (标记菜单项)

函数 UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck );
nIDCheckItem 是与 nCheck 相关的参数取值
nCheck 是 MF_BYCOMMAND or MF_BYPOSITION 和 MF_CHECKED or MF_UNCHECKED
的和

2 给菜单前添位图 (标记菜单的功能等)

函数 BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags,
       const CBitmap* pBmpUnchecked,
       const CBitmap* pBmpChecked );

nPosition 与   nFlags 相关 nFlags 为 MF_BYCOMMAND 或 MF_BYPOSITION

注意CBitmap 是资源定义的应该是类变量 而不是 局部变量

CBitmap 13*13 才能符合标记大小

技巧:给出一个求 系统中定义大小的函数 int GetSystemMetrics(int nIndex )
nIndex 为系统的ID 查询MSDN (记得 GetTextMetrics 是文本信息 字体的平均宽度和高度的求法)
CString :: Format("%d,%d")格式处理内容到 str字串中 和 C中的printf();用法差不多~!

3 标明菜单可用不可用(变灰+是否真的可点击来发送命令消息)

使用函数CMenu::UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );
同样参数 nIDEnableItem 与参数 nEnable 有关
nEnable MF_BYCOMMAND or MF_BYPOSITION 与 MF_DISABLIE   or MF_ENABLE 也可以变为灰色 MF_GRAYED

需要注意的是 想让手动对菜单项进行操作 需要在(Frame)构造函数中将一个值( m_bAutoMenuEnable )
初始化为 FALSE !   m_bAutoMenuEnable 提供了MFC自动识别是否菜单项可用(通过检测是否为MFC添加了
消息响应函数)!!


4 加载新的菜单 或 更换新的菜单

有关函数 SetMenu(CMenu *menu);返回值为 修改之前的菜单 ;注意是指针!!
忽忽 使用SetMenu(NULL);   就可以取消菜单哦~    
不要忘了 Menu 也是资源 需要定义在类成员中 不然要出错的!!!
使用Menu.LoadMenu( IDNo. );   


MFC的菜单消息更新机制~(可以通过此来修改菜单是否可用等)


菜单的运行是一个快速循环的过程,依赖于CN_UPDATE_COMMAND_UI消息 谁捕获了CN_UPDATE_COMMAND_UI消息
MFC将会创建一个对象 CCmdUI (UI是用户接口的英文缩写 User-I****记不住了) CCmdUI 先与子菜单索引位
置0,菜单项索引位置0开始 循环一次向下推进一次(当然CPU的速度很快 几乎就是一瞬间的事情)而我门通过
对CN_UPDATE_COMMAND_UI消息的捕获 来控制菜单的属性;

实现的方法:通过手工或 Classwizard 来添加一个命令ID的响应 (不要使用原来用的COMMAND而使用UPDATE_COMMAND_UI) 利用所添加的函数 传进来的参数 pCmdUI(是指针)来进行属性的操作!

//工具栏图标的索引记数顺序是:从做至右从0开始,分隔符也算索引号
//当工具栏与菜单ID一样时 就可以实现其快捷方式(点击后产生相同的消息映射)

提示 可以去看看 pCmdUI 中的成员!


增加右键弹出菜单的功能(俩种方法)

1 通过编译器来添加右键菜单

点击VC6.0的Project(工程)->Add to Project(增加到工程)->Components and Controls.....
观察它为我门增加了什么进入代码:A,一个菜单资源;B,在派生View类中增加OnContextMenu()函数
(至于怎么对右键进行消息扑获 未研究清楚)
所以我门使用对右键的消息响应 在其中增加菜单就可以手动来加入菜单!

2 手动来加入右键POP菜单!

先添加一个对右键响应的消息处理 OnRButtonDown函数
在OnRButtonDown函数内 例子代码:

CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu *pPopup=menu.GetSubMenu(0);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
    this);

对函数 TrackPopupMenu()说明:

CMenu::BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );

参数:
nFlags 是指一个右键的菜单的弹出方式(包括他与鼠标的位置等,习惯参数为TPM_LEFTALIGN | TPM_RIGHTBUTTON 意思是 在鼠标右边 向下弹出!)

x,y 是坐标点 (注意是 屏幕的坐标点而不是客户区的坐标 需要使用
void ClientToScreen( LPPOINT lpPoint ) const来转换成客户区!

CWnd* pWnd 是指这个菜单的拥有者 使用 this 就可以

lpRect 是点击多大区域有效 (NULL)

对ClientToScreen()分析 是 CWnd 的成员函数
是将 现在的Client 坐标 转化为 Screen 坐标!
而 TrackPopupmenu()是使用 Screen 坐标的!

 

对 Popup 菜单的响应顺序 View -> Frame

技巧: 得到父窗口指针 使用函数 Cwnd::CWnd* GetParent( ) const;


有关于资源声明成类成员的的另一种发现::

之所以声明资源为类成员 是因为当他是局部的变量时 在执行完有关函数
后,会被析构(释放空间) 而发现了一个大部分资源类都有的函数   Detach()
可以把资源与相关的句柄断开 使他不会发生析构 动态添加菜单等资源时,必须这
么使用 而不是声明资源为类的成员~;(那就不叫动态了!!)

 

菜单的动态操作:


有关函数:

在菜单尾部添加

CMenu::BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );

CMenu::BOOL AppendMenu( UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );

nFlags 是所要添加菜单的种类
nIDNewItem 将会根据 nFlags 的变化而变化

这是一个很复杂的函数 在教程里我门使用这个函数:
nFlags 为一个Popup的弹出菜单 时   nIDNewItem 是弹出菜单的句柄 menu.m_hMenu   但是参数要求是一个
UINT 类型 我门需要强制转换一下(原理不祥!)

CMenu::CreatePopupMenu() 创建一个空的弹出菜单 与 菜单资源相关连~

在原有菜单后添加弹出(Popup)菜单的完整代码:

CMenu menu;    //先要创建一个新的资源菜单 然后使用这个资源加载在原来菜单的后面
menu.CreatePopupMenu();
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"daigong"); // 这里因为是添加Popup菜单
               // 当然指获取 菜单的指针就OK
               // 而不是子菜单
menu.Detach(); //使用了Detach 不让资源析构


在菜单间插入新的菜单:

函数:CMenu::BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );

nFlags决定了nPosition 的取值(命令or索引号)也控制了插入的形式


AppendMenu()也好 InsertMenu()也好 都需要为新的菜单项给定一个 ID号 以便用来以后的消息响应!
还有就是对什么形式的菜单操作就获取什么菜单的对象或指针 (例:增加Popup菜单需要 GetMenu()
而 增加子菜单中的菜单项则需要 GetMenu()->GetSubMenu( nPos )来调用函数)

 


删除菜单

函数
CMenu::BOOL DeleteMenu( UINT nPosition, UINT nFlags );

同上 不用在说了吧 T_T


手动添加消息响应函数(重要哦 可以不依赖编译器的方法!!!)


1   先要定义命令的ID 在FileView里头文件 Resource.h 中添加 例:#define   IDM_DAIGONG 117
如果原来有ID 那么 就不需要添加了;

2   找到相应的头文件(通常是Frame类中因为对是对菜单进行消息响应么!)

找到(一般会在头文件的最后)
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   // NOTE - the ClassWizard will add and remove member functions here.
   //     DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};     
来添加消息函数原形:afx_msg void OnDaigong();

代码变为:
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   // NOTE - the ClassWizard will add and remove member functions here.
   //     DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
afx_msg void OnDaigong();   //后来增加的代码
DECLARE_MESSAGE_MAP()
};

3   添加消息映射!

在代码区(.cpp文件)里 找到:(通常是在前面)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
   // NOTE - the ClassWizard will add and remove mapping macros here.
   //     DO NOT EDIT what you see in these blocks of generated code !
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()


添加形式: ON_COMMAND(ID号,命令响应函数名)

这里要说明很多问题!! 第1 第二个参数上命令响应函数的名字 是没有()的
      第2 这个声明是结尾没有任何标点符号的(别以为我门写!!)

代码:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
   // NOTE - the ClassWizard will add and remove mapping macros here.
   //     DO NOT EDIT what you see in these blocks of generated code !
ON_WM_CREATE()
//}}AFX_MSG_MAP
ON_COMMAND(ID_EDIT_COPY,OnEDIT_COPY) //后来添加的! 可要没有标点哦!
END_MESSAGE_MAP()
4   在CPP文件里写函数体

void CMainFrame::OnEDIT_COPY()
{
MessageBox("OK");

}

技巧:如果忘记了怎么写消息映射 可以使用ClassWzard 添加一个消息响应函数 用此来引导我门去写
消息响应 不过要注意一点!!::在CPP文件中消息映射放到宏外(注意一定要放到宏外面,因为CLASSWIZARD发现菜单删除了,同时要把其宏对里的消息映射也删除掉的)!

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页