我们选择IDM_TEST(这个是我们自己建的一个菜单ID),我们选择消息响应COMMAND,然后点击Add Function添加一个函数。编辑代码。在我们所有的类中,都可以响应菜单项。而哪一个类最先对菜单项进行响应呢?
我们对 CMenuApp类
标准消息:
命令消息:
通告消息:
View类查找一下,如果没有对菜单命令进行响应,它会把命令交给文档类。Doc 进行处理。如果没有响应,文档类会把消息交还给View类,再交还给CMainFrame,查找,如果没有进行响应,那么最后交给App这个应用程序类进行处理。
标记菜单:
什么是子菜单: 我们通常看到的一个整列的菜单,就是子菜单。菜单的第一个菜单项,索引为0;
我们利用一个方法获取框架窗口菜单栏的指针。
CMenu* GetMenu( ) const;
它的返回值是一个指向CMenu的一个指针,CMenu封装了跟菜单有关的操作。
那么我们看一下CMenu
在这个函数中,它给我们提供了一个函数,可以获取子菜单。
CMenu* GetSubMenu( int nPos ) const;
他有一个参数,他们根据我们菜单的弹出位置去访问。
他们两个函数返回的指针是不同的。GetMenu返回的是指向菜单栏的指针
UINT CheckMenuItem( UINT nIDCheckItem, UINT nCheck );
第一参数的含义是由第二个参数决定的。 获得菜单项的索引 或者ID号。
例:
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND|MF_CHECKED);
缺省菜单项:
用BOOL SetDefaultItem( UINT uItem, BOOL fByPos = FALSE );
这个来获取菜单。
例:GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN,FALSE);
图形标记菜单的创建:
CMenu中有这样的一个菜单函数:
BOOL SetMenuItemBitmaps( UINT nPosition, UINT nFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked );
后便两个参数都是CBitmap的指针,一个是我们选中的时候的一个位图,一个是我们取消时候的一个位图。
我们在资源里new一个位图。
我们为CMainFrame加一个成员变量m_bitmap;
m_bitmap.LoadBitmap(IDB_SB); //加载资源里的位图
GetMenu()->GetSubMenu(0)->SetMenuItemBitmaps(0,MF_BYPOSITION,&m_bitmap,&m_bitmap);
当我们运行的时候我们会发现,不是我们想要的位图,这是什么原因呢? 原因就是我们自己做的位图太大了。
这时候我们需要用一个函数去获取菜单标记的大小。
int GetSystemMetrics(
);
获取系统的一些信息的度量。
SM_CXMENUCHECK,
SM_CYMENUCHECK
这两个参数,是缺省标记位图的大小。
我们先定义一个CString的对象,输出显示出来
CString str;
str.Format("x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),GetSystemMetrics(SM_CYMENUCHECK));
MessageBox(str);
Format成员函数,可以把内容按照一定的格式,格式化到CString的对象当中。这里我们通过上边的函数,获取到了位图标记的大小。
屏蔽菜单项:
我们可以利用CMenu的一个函数
UINT EnableMenuItem( UINT nIDEnableItem, UINT nEnable );
例:
GetMenu()->GetSubMenu(0)->EnableMenuItem(ID_FILE_OPEN,MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
//
GetMenu* mmenu=GetMenu();
GetSubMenu* submenu=mmenu->GetSubMenu(0);
submenu->EnableMenuItem(ID_FILE_OPEN,MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
当我们运行之后,会发现我们想要的效果并没有实现。
MFC给我们提供了一个命令更新的机制,所以我们的CMenu就不起作用了。
如果想要自己控制菜单项的使用不适用,就在CMainFrame的构造函数中将m_bAutoMenuEnable设置为FALSE。
如何将整个菜单取消掉:
我们用这样一个函数
BOOL SetMenu( CMenu* pMenu );
参数如果是NULL,程序中菜单被移走。
例:
SetMenu(NULL);
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
上边的代码,我们把菜单移走,然后又重新加载显示了菜单。
在这里,CMenu对象是局部的对象,会导致程序出问题。
这个时候需要把CMenu设置为一个成员变量,或者利用 Detach这样一个函数。menu.Detach();
命令更新机制:
菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI这个消息,谁捕获了CN_UPDATE_COMMAND_UI这个消息,MFC就在其中创建一个CCmdUI对象。我们可以手工或者利用ClassWizard在消息映射中添加ON_UPDATE_COMMAND_UI宏来捕获CN_UPDATE_COMMAND_UI消息。
在我们的程序中,我们想要让某个菜单项可用或者不可用。我们可以在ClassWizard当中选择这个菜单项,在Messages:中选择UPDATE_COMMAND_UI增加一个函数,编辑代码。它会在消息映射中给我们增加一个ON_UPDATE_COMMAND_UI宏,这个宏就是用来处理CN_UPDATE_COMMAND_UI这个消息的。我们需要用命令跟新的函数进行处理。
CCmdUI这个类中提供了许多函数,一个菜单项是否可以使用,是否有标记,改变命令UI接口的文本。UI(user-interface)即用户接口。
在后台所做的工作是:操作系统发出WM_INITMENUPOPUP消息,然后MFC的基类,如CFrameWnd接管。它创建一个CCmdUI对象,并与第一个菜单相关联,调用对象的一个函数DoUpdate();这个函数发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针,当我们对于第一个菜单项更新完成以后,我们用CN_UPDATE_COMMAND_UI进行处理,有没有这样的一个宏对消息进行处理。如果有,就会执行相应的命令响应函数,我们可以用CCmdUI的指针调用SetCheck让他可以使用。更新完成只有,同一个CCmdUI对象就设置为与第二个菜单项相关联,这样的顺序进行,知道完成所有菜单项。
例:
virtual void Enable( BOOL bOn = TRUE );
这个函数用来使菜单项可用。
CCmdUI有很多的成员变量,可以获取菜单的 索引
在做程序的时候,我们经常用到右键弹出菜单的功能。这些VC++都给我们做好了,我们点击工程(Project)->添加一个工程(Add To Project)->(Comporents and Controls)
Pop-up Menu
BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );
这个函数可以显示弹出菜单。
例:
我们需要把客户区坐标转换成屏幕坐标。
void ClientToScreen( LPPOINT lpPoint ) const;
可以完成客户区坐标与屏幕坐标的一个转换。
void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
}
下面我们对菜单里的一个菜单项进行命令捕获。
发现,由于我们的指针为this,即指向View类的指针,所以框架类MainFrame是不能对这个消息进行相应。
对于一个子窗口来说,他有最先响应消息的权限。
动态的添加删除 响应一个菜单。
我们可以在CMainFrame OnCreate函数中添加菜单项。动态添加菜单,我们可以用CMenu的一个成员函数。
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
BOOL AppendMenu( UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
第一个参数可以去多个值,有参数列表。第二个参数指示了新的菜单项的命令ID,如果是弹出菜单,它就被设置成一个菜单句柄,我们菜单的句柄如何去获取,所有的资源相关的类,内部都有一个成员变量,保存了和他相关的句柄,当然我们的菜单资源也有一个成员变量,保存于他相关的菜单资源的句柄。。如果是分隔栏,那么它就忽略了。第三个参数,它指示菜单项的名称。
我们先创建一个弹出菜单。
BOOL CreatePopupMenu( );
例:
我们定义一个CMenu。
CMenu menu;
menu.CreatePopupMenu();
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"SB"); //这个地方我们赋了一个句柄,但是他需要一个UINT这样的一个类型,我们需要在前边做一个强制转换。
//GetMenu()在这里的作用: 要AppendMenu 我们需要获取当前和我们这个框架装口相关联菜单栏的指针。
menu.Detach(); //因为我们在这里定义的是一个局部变量 CMenu,所以会发生错误,所以我们需要把它定义为成员变量,或者利用Detach这个函数,将句柄和我们的CMenu这个对象断开。
如果说我们要插入一个菜单:
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL );
BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );
第一个参数指示了菜单的菜单项,ID号或者索引号。 其他的参数跟我们AppendMenu是一样的。
例:
menu.CreatePopupMenu();
GetMenu()->InsertMenu(2,MF_BYPOSITION|MF_POPUP,(UINT)menu.m_hMenu,"SB");
menu.Detach();
如果我们想在弹出菜单中添加菜单项,同样可以采用AppendMenu
例:
menu.AppendMenu(MF_STRING,111,"NO SB!");
menu.AppendMenu(MF_STRING,222,"SB OK?");
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,333,"你怎么这么SB");
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,MF_BYCOMMAND|MF_STRING,444,"SB透了");
删除子菜单:
BOOL DeleteMenu( UINT nPosition, UINT nFlags );
例:
GetMenu()->DeleteMenu(1,MF_BYPOSITION);
删除菜单项。例子:
GetMenu()->GetSubMenu(0)->DeleteMenu(2,MF_BYPOSITION);
对动态插入的菜单进行命令响应:
我们在文件 FileView 中有个Header Files
Resource.h定义了资源的ID。我们可以自己添加一个资源ID
例:#define IDM_SB
1.首先在头文件中,做命令响应函数的原型。
例:
//{{AFX_MSG(CMainFrame)
2.接下来,消息映射,对于命令消息,我们通过ON_COMMAND宏来完成。
例:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
END_MESSAGE_MAP()
3.最后函数。命令响应函数的实现。
例:
void CMainFrame::OnSB()
{
}
接下来,我们做一个电话本的实例:
现在我们要把我们输入的内容显示出来。
当我们CMainFrame的这个窗口和菜单创建成功之后,当我们再次去修改这个菜单的时候,我们需要让这个菜单栏重绘。
void DrawMenuBar( );
我们用上边这个函数来重画。
如果菜单栏Windows已经创建窗口之后,被改变,我们用这个函数来重画改变菜单栏。
例:
m_menu.CreatePopupMenu(); //创建一个空的菜单
//获得菜单 由于菜单栏在框架类中,我们想要获取到框架类的菜单
GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"PhoneBook");
GetParent()->DrawMenuBar(); //因为DrawMenuBar是在View类中调用的,而菜单栏在CMainFrame中而不再View类中,所以调用DrawMenuBar就不起作用了,所以我们要用父窗口的指针来调用这个函数。
使窗体重绘的函数
void Invalidate( BOOL bErase = TRUE );
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;
例:
void CMenu2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
}
C++的类去掉C就是名字。如CMenu2View,名字就是Menu2View。而CMainFrame是个例外。