菜单的工作原理及编写应用
今天赶了2场酒,累死了,但还是坚持看完了第6课,写了点笔记,大家鼓励鼓励嘛,不要看看就走了
1、菜单如果选择了pop-up属性,是不能响应命令的
菜单idm_test--框假类cmainframe--消息command--响应函数OnTest()
|
其实可以为不同的类定制这样的消息响应,响应顺序为:view-doc-mainframe-app
2、消息的分类
? 标准消息
除WM_COMMAND之外,所有以WM_开头的消息。
从CWnd派生的类,都可以接收到这类消息。
? 命令消息
来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。
从CCmdTarget派生的类,都可以接收到这类消息。
? 通告消息
由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。
从CCmdTarget派生的类,都可以接收到这类消息。
CWnd : CCmdTarget : CObject
从CWnd派生的类,都可以接收到标准消息。
从CCmdTarget派生的类,都可以接收到命令消息。
于是从CWnd派生的类既可以接收标准消息,也可以接收命令消息。
而app类和doc类都是直接从CCmdTarget派生的
命令消息机制与标准消息类似,只是在.cpp文件中其消息宏有点不一样ON_COMMAND(IDM_TEST,OnTest)
3、命令消息的路由和标准消息不一样
AfxWndProc //mfc将窗口过程函数替换为此函数
|
AfxCallWndProc //
|
WindowProc //CWnd类成员函数
|
OnWndMsg //判断消息类型
___________|____________
| |
OnCommand OnNotify //命令消息(完成命令消息路由) , 通告消息
|__________ ___________|
|
OnCmdMsg
OnCommand路由过程:mainframe(无判断直接交)->view->doc->view->mainframe->app
4、标记菜单(有钩号)
菜单结构
3 300 301 302 303 304
2
1
0
0 1 2 3 4
5、CWnd::GetMenu
CMenu* GetMenu() const; //获取框架上菜单的指针
CMenu : CObject
CMenu::GetSubMenu
CMenu* GetSubMenu(int nPos)const; //获取子菜单的指针
CMenu::CheckMenuItem
UINT CheckMenuItem(UINT nIDCheckItem, UINT nCheck); //第一个参数的含义由第二个参数决定
菜单id MF_BYCOMMAND //按id访问
菜单索引号 MF_BYPOSITION //按索引访问
为某菜单增加复选标记
GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
6、设置缺省菜单项
CMenu::SetDefaultItem
BOOL SetDefaultItem(UINT uItem, BOOL fByPos = FALSE); //第一个参数的含义由第二个参数决定
菜单id FALSE //按id访问
菜单索引号 TRUE //按索引访问
分隔栏也占一个索引号
一个子菜单只能有一项缺省菜单项,以后一个定义的缺省定义为准
7、图形菜单
CMenu::SetMenuItemBitmaps
BOOL SetMenuItemBitmaps(UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked,
const CBitmap* pBmpChecked); //第一个参数的含义由第二个参数决定
//菜单选择图,取消选择图
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);
位图大小:13*13
8、int GetSystemMetrics(int nIndex); //按给出的标记获取系统相应的信息
9、菜单变灰
CMenu::EnableMenuItem
UINT EnableMenuItem(UINT nIDEnableItem, UINT nEnable); //第一个参数的含义由第二个参数决定
1 类构造函数中:m_bAutoMenuEnable=FALSE; //变量无须定义,影响所有菜单
2 GetMenu()->GetSubMenu(0)->EnableMenuItem(0,MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
10、CWnd::SetMenu
BOOL SetMenu(CMenu* pMenu); //参数为菜单指针,NULL,则整个菜单条不见了
SetMenu(NULL); //菜单不见了
CMenu menu; //要设为成员变量,否则出错,或者用Detach()
menu.LoadMenu(IDR_MAINFRAME_MENU); //载入菜单资源
SetMenu(&menu); //菜单回来了
menu.Detach(); //后面加的
SetMenu()设置的是一个新的菜单,必须用Detach()断开菜单句柄和CMenu对象的联系
Detach()将HMENU这个句柄从CMenu对象中断开,这样,当CMenu对象被析构的时候,菜单不会被销毁,这样就避免了因为用的是局部变量对象而产生的错误
11、菜单项命令更新机制
菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,MFC就在其中创建一个CCmdUI对象。我们可以通过手工或利用ClassWizard在消息映射中添加ON_UPDATE_COMMAND_UI宏来捕获CN_UPDATE_COMMAND_UI消息。
在后台所做的工作是:当要显示一个菜单的时候,操作系统发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd接管。它创建一个CCmdUI对象,并与第一个菜单项相关联,调用CCmdUI对象的一个成员函数DoUpdate()。这个函数发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。同一个CCmdUI对象就设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。
更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。
为剪切菜单项添加CN_UPDATE_COMMAND_UI消息响应函数OnUpdateEditCut(CCmdUI* pCmdUI){}
CCmdUI::SetText
virtual void SetText(LPCTSTR lpszText); //改变菜单项的文本
CCmdUI::Enable
virtual void Enable(BOOL bOn = TRUE); //设置菜单可用/不可用,缺省参数为TRUE
OnUpdateEditCut(CCmdUI* pCmdUI){pCmdUI->Enable();}//剪切菜单项就可用了/波及工具栏响应按钮
为什么剪切菜单项可用的时候相应工具栏就能用了呢,他们是如何关联的呢
靠,原来剪切菜单项的id和相应工具栏的id是一模一样的,id_edit_cut
以后要让工具栏与菜单项相关联,只要将id设为一致的就行了
pCmdUI->m_nID 保存了与当前CCmdUI对象相关联的菜单项的id
pCmdUI->m_Index 保存了与当前CCmdUI对象相关联的菜单项的索引值
void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI)
{
if(ID_FILE_NEW==pCmdUI->m_nID) //如果改为索引判断,则不能和菜单成功关联,原因是
pCmdUI->Enable(FALSE); //工具栏另有索引,so,最好用id
}
12、右键菜单
工程-增加工程-组件和控件,选择增加一个 pop-up Menu ,选择增加到哪个类中,如view
1 菜单资源中出现了一个新的菜单
2 .cpp中增加了一个新的函数OnContextMenu(),里面已经有代码了
CWnd::OnContextMenu
afx_msg void OnContextMenu(CWnd* pWnd, CPoint pos);
主要在右键按下时被框架调用,随后可以用TrackPopupMenu()显示菜单
CMenu::TrackPopupMenu
BOOL TrackPopupMenu(UINT nFlags,int x,int y,CWnd* pWnd,LPCRECT lpRect=NULL);
相对鼠标显示位置,鼠标当前坐标,哪个窗口的菜单,响应点击区域
显示菜单的时候,显示坐标为屏幕坐标,而不是客户区坐标
手动增加一个
1 建立菜单资源,设置、记住id
2 添加右键消息和处理函数
3 在函数中添加和OnContextMenu()类似的内容,当然也调用TrackPopupMenu()
CWnd::ClientToScreen //转换客户区坐标为屏幕坐标
void ClientToScreen(LPPOINT lpPoint)const;
void ClientToScreen(LPRECT lpRect)const; //结果输出至参数
void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
ClientToScreen(&point); //转换坐标
CMenu menu;
menu.LoadMenu(IDR_MENU1); //载入资源
CMenu *pPopop = menu.GetSubMenu(0); //获取子菜单指针
pPopop->TrackPopupMenu(IPM_LEFTALIGN | IPM_HIGHIBUTTON,point.x,point.y,this);
CView::OnRButtonDown(nFlags,point); // 这句原来有的
}
13、为右键菜单项增加命令响应
右键菜单资源选择类向导-不建立类-选择COMMAND消息并添加处理函数
14、动态添加菜单
CMenu::AppendMenu //添加一个菜单项到现有菜单的末尾
BOOL AppendMenu(UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszNewItem=NUll);
BOOL AppendMenu(UINT nFlags,UINT nIDNewItem,const CBitMap* pBmp);
菜单类型,菜单对象索引,菜单的名称
CMenu::CreatePopupMenu
BOOL CreatePopupMenu(); //创建一个空的弹出菜单,并把菜单资源和菜单对象相关联起来
CMenu menu; //要定义成成员变量,或者用menu.Detach(),否则对象离开函数被析构后产生错误
menu.CreatePopupMenu(); //创建空菜单
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun"); //把菜单添加到现有菜单末尾
menu.Detach(); //后面加的 |--->这里用了菜单对象的句柄,然后强制类型转换
15、插入菜单
CMenu::InsertMenu
BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem=0,LPCTSTR lpszNewItem=NULL);
BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem,const CBitmap* pBmp);
GetMenu()->InsertMenu(2,MF_BYPUSITION | MF_POPUP,(UINT)menu.m_hMenu,"WinSun");//插入菜单
16、插入菜单项
CMenu menu; //要定义成成员变量,或者用menu.Detach(),否则对象离开函数被析构后产生错误
menu.CreatePopupMenu(); //创建空菜单
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"WinSun"); //把菜单添加到现有菜单末尾
menu.AppendMenu(MF_STRING,111,"hello");
menu.AppendMenu(MF_STRING,112,"weixin");
menu.AppendMenu(MF_STRING,113,"mybole");
menu.Detach(); //后面加的 |--->这里用了菜单对象的句柄,然后强制类型转换
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Welcome"); //在文件菜单中添加菜单项
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,
MF_BYCOMMAND | MF_STRING,115,"WinSun");//插入菜单项
17、删除菜单
CMenu::DeleteMenu
BOOL DeleteMenu(UINT nPosition,UINT nFlags);//
删除哪个菜单,哪个菜单项,要看你用哪个菜单的指针去调用该函数
GetMenu()->DeleteMenu(1,MF_BYPUSITION);
GetMenu()->GetSubMenu(0)->DeleteMenu(ID_FILE_OPEN,MF_BYCOMMAND);
18、手动添加菜单项响应
1 首先创建资源id
Resource.h --> #define IDM_HELLO 111
2 把原来写好的插入菜单项的代码中资源id改为定义id
menu.AppendMenu(MF_STRING,IDM_HELLO,"hello");
3 手工添加消息原形和消息映射
.h 中添加消息响应函数宏 afx_msg void OnHello(); //消息原形
.cpp 中添加消息映射 ON_COMMAND(IDM_HELLO,OnHello) //不要加标点符号
添加响应函数 void C****::OnHello(){}
19、前面都是在框架类中操作,直接使用GetMenu()即可获取属于框架类的菜单指针,现在在view中调用菜单,必须首先获取框架类指针,如何获取呢,可以用GetParent()获取父窗口指针的方式得到
在非mainframe类OnCreate()中添加菜单,菜单不会立即显示,必须使用菜单重绘函数DrawMenuBar()
CWnd::DrawMenuBar
voidb DrawMenuBar(); //重绘菜单栏
GetParent()->DrawMenuBar(); //在view类中对属于框架类的菜单操作,必须通过框架类指针使用
//否则不自动显示菜单,因为菜单栏并不属于view类
20、窗口重绘
CWnd::Invalidate
void Invalidate(BOOL bErase = TRUE); //窗口重绘
21、CString::Find
int Find(TCHAR ch)const; //返回一个基于零的所在字符的索引值
int Find(LPCTSTR lpszSub)const;
int Find(TCHAR ch,int nStart)const;
int Find(LPCTSTR pstr,int nStart)Const;
CStringArray : CObject
CStringArray支持CString对象数组,可动态增加
CStringArray调用的几乎使CObArray对象的函数
CStringArray的增加对象函数add()用的就是CObArray对象的add()函数
CObArray::Add
int Add(CObject* newElement); //增加元素
CObArray::GetAt
CObject* GetAt(int nIndex)const; //取出元素
CObArray::GetSize //元素个数
22、重载OnCommand(),自定义处理命令消息
CWnd::OnCommand
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
参数wParam为4字节无符号整型,前面两个字节就是低字节区,低字节区标识了命令id
有一个宏可以取到低字节区
WORD LOWORD{DWORD dwValue};
取高字节区
WORD HOWORD{DWORD dwValue}; //从一个32位的值当中获取高字节区的值
CFrameWnd::GetActieView
CView* GetActiveView()const; //获取当前视类的一个指针
想直接通过C***View view;的方式获取已经被与mainframe等关联起来的view对象是不可能的,应该通过上面的函数获取已经存在的被与其他类关联起来的view类指针
CMenu2View *pView=(CMenu2View)GetActiveView(); //获取当前view类的指针