Lesson6 菜单
state:finished
1.AfxMessageBox应用程序框架的MessageBox函数
2.接收菜单命令消息的顺序,View类->Doc类->MainFrame类->App类
3.消息的分类:
标准消息
除WM_COMMAND之外,所有以WM_开头的消息。
从CWnd派生的类,都可以接收到这类消息。
命令消息
来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。
在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,
通过消息的wParam参数识别。
从CCmdTarget派生的类,都可以接收到这类消息。(CWnd是CCmdTarget类的子类)
通告消息
由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,
为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以
WM_COMMAND形式呈现。
从CCmdTarget派生的类,都可以接收到这类消息。
4.源代码的变化
MenuView.h
------------------------------------------------------------------------------------
protected:
//{{AFX_MSG(CMenuView)
afx_msg void OnTest(); //命令消息处理函数原型的声明
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
-------------------------------------------------------------------------------------
MenuView.CPP
-------------------------------------------------------------------------------------
BEGIN_MESSAGE_MAP(CMenuView, CView)
//{{AFX_MSG_MAP(CMenuView)
ON_COMMAND(IDM_TEST, OnTest) //使用ON_COMMAND宏将我们的菜单ID和消息
//}}AFX_MSG_MAP //响应函数联系起来
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
---------------------------------------------------------------------------------------
5.创建标记菜单
CWnd::GetMenu
MSDN
-----------------------------------------------------------------------------------------
CWnd::GetMenu
Retrieves a pointer to the menu for this window.
CMenu* GetMenu( ) const; //获取框架窗口菜单栏的指针
---------------------------------------------------------------------------------------
CMenu派生自CObject,封装了和菜单有关的操作,封装了一个菜单句柄
CMenu::GetSubMenu
MSDN
-----------------------------------------------------------------------------------------
CMenu::GetSubMenu //返回子菜单的操作
Retrieves the CMenu object of a pop-up menu.
CMenu* GetSubMenu(
int nPos //弹出菜单的位置
) const;
----------------------------------------------------------------------------------------
CMenu::CheckMenuItem
MSDN
--------------------------------------------------------------------------------------
CMenu::CheckMenuItem
Adds check marks to or removes check marks from menu items in the pop-up menu.
UINT CheckMenuItem(
UINT nIDCheckItem,
UINT nCheck
);
nIDCheckItem
Specifies the menu item to be checked, as determined by nCheck.
nCheck
Specifies how to check the menu item and how to determine the item's position in the
menu. The nCheck parameter can be a combination of MF_CHECKED or MF_UNCHECKED with
MF_BYPOSITION or MF_BYCOMMAND flags. These flags can be combined by using the bitwise
OR operator. They have the following meanings:
MF_BYCOMMAND Specifies that the parameter gives the command ID of the existing menu
item. This is the default.
MF_BYPOSITION Specifies that the parameter gives the position of the existing menu
item. The first item is at position 0.
MF_CHECKED Acts as a toggle with MF_UNCHECKED to place the default check mark next
to the item.
MF_UNCHECKED Acts as a toggle with MF_CHECKED to remove a check mark next to the item.
---------------------------------------------------------------------------------------
加上选择标记:
MainFrm.cpp
--------------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED);
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYPOSITION | MF_CHECKED);
}
--------------------------------------------------------------------------------------
6.创建缺省的菜单项
CMenu::SetDefaultItem
MSDN
-----------------------------------------------------------------------------------------
CMenu::SetDefaultItem
Sets the default menu item for the specified menu.
BOOL SetDefaultItem(
UINT uItem,
BOOL fByPos = FALSE
);
----------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
//GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);
}
--------------------------------------------------------------------------------------
一个子菜单项中不能有两个缺省的菜单
7.图形标记菜单的创建
CMenu::SetMenuItemBitmaps
MSDN
----------------------------------------------------------------------------------------
CMenu::SetMenuItemBitmaps
Associates the specified bitmaps with a menu item.
BOOL SetMenuItemBitmaps(
UINT nPosition,
UINT nFlags, //MF_BYCOMMAND or MF_BYPOSITION
const CBitmap* pBmpUnchecked, //标记取消时候显示的位图
const CBitmap* pBmpChecked //点中的时候显示的位图
);
------------------------------------------------------------------------------------------
获取图形标记菜单标记的大小
GetSystemMetrics
-----------------------------------------------------------------------------------------
MSDN
GetSystemMetrics
The GetSystemMetrics function retrieves various system metrics (widths and heights of
display elements) and system configuration settings. All dimensions retrieved by
GetSystemMetrics are in pixels.
int GetSystemMetrics(
int nIndex //SM_CXMENUCHECK, SM_CYMENUCHECK Dimensions of the default
); //menu check-mark bitmap, in pixels.
------------------------------------------------------------------------------------------
8.使菜单项失效
CMenu::EnableMenuItem
MSDN
--------------------------------------------------------------------------------------------
CMenu::EnableMenuItem
Enables, disables, or dims a menu item.
UINT EnableMenuItem(
UINT nIDEnableItem,
UINT nEnable //MF_BYCOMMAND or MF_BYPOSITIONMF_DISABLED, MF_ENABLED, or MF_GRAYED
);
// NOTE: m_bAutoMenuEnable is set to FALSE in the constructor of
// CMainFrame so no ON_UPDATE_COMMAND_UI or ON_COMMAND handlers are
// needed, and CMenu::EnableMenuItem() will work as expected.
// 需要在CMainFrame的构造函数中写入 m_bAutoMenuEnable=FALSE 才能更新菜单,使上面的命令生效
---------------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
GetMenu()->GetSubMenu(0)->EnableMenuItem(ID_FILE_OPEN,MF_BYCOMMAND | MF_DISABLED
| MF_GRAYED);
}
--------------------------------------------------------------------------------------
9.取消系统菜单
CWnd::SetMenu
MSDN
---------------------------------------------------------------------------------------------
CWnd::SetMenu
Sets the current menu to the specified menu.
BOOL SetMenu(
CMenu* pMenu
);
-------------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
SetMenu(NULL);
CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
SetMenu(&menu);
menu.Detach(); //将句柄与C++局部变量断开
}
--------------------------------------------------------------------------------------
10. MFC对菜单项采用的命令更新机制
菜单项状态的维护是依赖于CN_UPDATE_COMMAND_UI消息,谁捕获CN_UPDATE_COMMAND_UI消息,
MFC就在其中创建一个CCmdUI对象。我们可以通过手工或利用ClassWizard在消息映射中添加
ON_UPDATE_COMMAND_UI宏来捕获CN_UPDATE_COMMAND_UI消息。
在后台所做的工作是:操作系统发出WM_INITMENUPOPUP消息,然后由MFC的基类如CFrameWnd
接管。它创建一个CCmdUI对象,并与第一个菜单项相关联,调用对象的一个成员函数
DoUpdate()。这个函数发出CN_UPDATE_COMMAND_UI消息,这条消息带有指向CCmdUI对象的指针。
同一个CCmdUI对象就设置为与第二个菜单项相关联,这样顺序进行,直到完成所有菜单项。
更新命令UI处理程序仅应用于弹出式菜单项上的项目,不能应用于永久显示的顶级菜单项目。
CCmdUI::Enable
MSDN
----------------------------------------------------------------------------------------
CCmdUI::Enable
Call this member function to enable or disable the user-interface item for this
command.
virtual void Enable(
BOOL bOn = TRUE
);
Parameters
bOn
TRUE to enable the item, FALSE to disable it.
-----------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
void CMainFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable();
}
void CMainFrame::OnUpdateFileNew(CCmdUI* pCmdUI)
{
// TODO: Add your command update UI handler code here
/*if (ID_FILE_NEW == pCmdUI->m_nID)
{
pCmdUI->Enable(FALSE);
}*/
if (0 == pCmdUI->m_nIndex) //工具栏的图标和菜单项是使用ID相关联的
{ //最好使用ID,而不使用索引
pCmdUI->Enable(FALSE);
}
}
--------------------------------------------------------------------------------------
11.增加右键弹出菜单
step1: Project->Add To Project->Components and Controls->
Visual C++ Components->Pop-up Menu
变化:1. 在资源项中加入了弹出菜单
2. 在CMenuView类中加入OnContextMenu(CWnd*, CPoint point)函数
CWnd::OnContextMenu
MSDN
--------------------------------------------------------------------------------------
CWnd::OnContextMenu
Called by the framework when the user has clicked the right mouse button
(right clicked) in the window.
afx_msg void OnContextMenu(
CWnd* pWnd,
CPoint pos
);
Remarks
You can process this message by displaying a context menu using the TrackPopupMenu.
---------------------------------------------------------------------------------------
CMenu::TrackPopupMenu
MSDN
---------------------------------------------------------------------------------------
CMenu::TrackPopupMenu
Displays a floating pop-up menu at the specified location and tracks the selection
of items on the pop-up menu.
BOOL TrackPopupMenu(
UINT nFlags, //弹出菜单时菜单显示的位置(相对鼠标的位置)
int x, //鼠标点击的位置坐标
int y,
CWnd* pWnd, //弹出菜单的拥有者
LPCRECT lpRect = 0 //指示一个矩形的区域,在这个矩形区域内点击,弹出菜单不消失
); //在矩形区域之外点击弹出菜单消失
-----------------------------------------------------------------------------------------
CWnd::ClientToScreen
MSDN
------------------------------------------------------------------------------------------
CWnd::ClientToScreen
Converts the client coordinates of a given point or rectangle on the display to
screen coordinates.
void ClientToScreen(
LPPOINT lpPoint
) const;
void ClientToScreen(
LPRECT lpRect
) const;
-------------------------------------------------------------------------------------------
MenuView.cpp
-------------------------------------------------------------------------------------------
void CMenuView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CMenu* pPopup=menu.GetSubMenu(0);
ClientToScreen(&point); //客户区坐标转换为屏幕坐标
//pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
//this);//让View窗口拥有弹出菜单
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,
GetParent()); //让框架窗口拥有弹出菜单
CView::OnLButtonDown(nFlags, point);
}
//DEL void CMenuView::OnShow()
//DEL {
//DEL // TODO: Add your command handler code here
//DEL MessageBox("View Show!");
//DEL }
void CMenuView::OnShow()
{
// TODO: Add your command handler code here
MessageBox("View Show!");
}
-------------------------------------------------------------------------------------------------
小结:消息总是先由顶层窗口响应,如果顶层窗口类没有消息响应函数,则立即交由底层窗口类响应
12.添加修改删除动态菜单
(1)动态添加菜单
CMenu::AppendMenu
MSDN
-------------------------------------------------------------------------------------------------
CMenu::AppendMenu //可以将菜单或菜单项添加到现有菜单的末尾
Appends a new item to the end of a menu.
BOOL AppendMenu(
UINT nFlags, //MF_POPUP 表示添加的是弹出菜单,MF_SEPARATOR分隔栏,MF_STRING 字符串
UINT_PTR nIDNewItem = 0, //新菜单项的命令ID
LPCTSTR lpszNewItem = NULL //指示菜单的名称
);
BOOL AppendMenu(
UINT nFlags,
UINT_PTR nIDNewItem,
const CBitmap* pBmp
);
----------------------------------------------------------------------------------------------------
(2)创建弹出菜单
CMenu::CreatePopupMenu
MSDN
---------------------------------------------------------------------------------------------------
CMenu::CreatePopupMenu
Creates a pop-up menu and attaches it to the CMenu object.
BOOL CreatePopupMenu( );
---------------------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
CMenu menu;
menu.CreatePopupMenu();
GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"VC");
menu.Detach(); //将句柄与C++局部变量断开
}
--------------------------------------------------------------------------------------
(3)插入菜单
CMenu::InsertMenu
MSDN
-----------------------------------------------------------------------------------------
CMenu::InsertMenu
Inserts a new menu item at the position specified by nPosition and moves other items
down the menu.
BOOL InsertMenu(
UINT nPosition,
UINT nFlags, //MF_BYCOMMAND or MF_BYPOSITION
UINT_PTR nIDNewItem = 0,
LPCTSTR lpszNewItem = NULL
);
BOOL InsertMenu(
UINT nPosition,
UINT nFlags,
UINT_PTR nIDNewItem,
const CBitmap* pBmp
);
-------------------------------------------------------------------------------------------
(4)在弹出菜单增加菜单项
-------------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
CMenu menu;
menu.CreatePopupMenu();
//GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,"VC");
GetMenu()->InsertMenu(2,MF_BYPOSITION | MF_POPUP,(UINT)menu.m_hMenu,"VC");
menu.AppendMenu(MF_STRING,111,"Hello");
menu.AppendMenu(MF_STRING,112,"VC");
menu.AppendMenu(MF_STRING,113,"MFC");//添加3个菜单项
GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,"Close");//在文件菜单添加菜单项
GetMenu()->GetSubMenu(0)->InsertMenu(ID_FILE_OPEN,MF_BYCOMMAND |
MF_STRING,115,"Windows");//在打开与新建之间加入菜单项
menu.Detach(); //将句柄与C++局部变量断开
}
--------------------------------------------------------------------------------------
(5)删除子菜单
CMenu::DeleteMenu
MSDN
-----------------------------------------------------------------------------------------
CMenu::DeleteMenu
Deletes an item from the menu.
BOOL DeleteMenu(
UINT nPosition,
UINT nFlags //MF_BYCOMMAND or MF_BYPOSITION
);
-----------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
CMenu menu;
menu.CreatePopupMenu();
GetMenu()->DeleteMenu(1,MF_BYPOSITION); //删除编辑菜单
GetMenu()->GetSubMenu(0)->DeleteMenu(ID_FILE_OPEN,MF_BYCOMMAND);//删除文件菜单的打开项
menu.Detach(); //将句柄与C++局部变量断开
}
--------------------------------------------------------------------------------------
(6)对动态添加的菜单项进行命令响应
step1:在Resource.h中添加资源ID
Resource.h
----------------------------------------------------------------
#define IDM_HELLO 111
----------------------------------------------------------------
step2:在头文件中添加命令响应函数原型
MainFrm.h
---------------------------------------------------------------------------
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 OnHello();
DECLARE_MESSAGE_MAP()
----------------------------------------------------------------------------
step3:在源文件中用ON_COMMAND宏关联消息响应函数
MainFrm.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
ON_COMMAND(IDM_HELLO,OnHello)
END_MESSAGE_MAP()
------------------------------------------------------------------------------
step4: 编写消息响应函数
MainFrm.cpp
-------------------------------------------------------------------------------
void CMainFrame::OnHello()
{
MessageBox("Hello!");
}
--------------------------------------------------------------------------------
13.制作电话本程序
菜单栏重绘
CWnd::DarwMenuBar
MSDN
---------------------------------------------------------------------------------------
CWnd::DrawMenuBar
Redraws the menu bar.
void DrawMenuBar( );
Remarks
If a menu bar is changed after Windows has created the window, call this function
to draw the changed menu bar.
-------------------------------------------------------------------------------------
窗口重绘
CWnd::Invalidate
MSDN
-----------------------------------------------------------------------------------
CWnd::Invalidate
Invalidates the entire client area of CWnd.
void Invalidate(
BOOL bErase = TRUE //表示背景会被擦除
);
-------------------------------------------------------------------------------------
CString::Find
MSDN
------------------------------------------------------------------------------
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类保存CString对象
------------------------------------------------------------------------------------------------
CWnd::OnCommand
This method is called by the framework when the user selects an item from a menu, when
a child control sends a notification message, or when an accelerator keystroke is translated.
virtual BOOL OnCommand(
WPARAM wParam,
LPARAM lParam );
wParam
The low-order word of wParam identifies the command ID of the menu item, control, or
accelerator. The high-order word of wParam specifies the notification message if the
message is from a control. If the message is from an accelerator, the high-order word
is 1. If the message is from a menu, the high-order word is 0.
lParam
Identifies the control that sends the message if the message is from a control. Otherwise,
lParam is 0.
-------------------------------------------------------------------------------------------------
获取当前视类的指针
CFrameWnd::GetActiveView
MSDN
------------------------------------------------------------------------------------------------
CFrameWnd::GetActiveView
This method obtains a pointer to the active view, if any, attached to a frame window, CFrameWnd.
CView* GetActiveView( )
const;
---------------------------------------------------------------------------------------------------
Menu2 Project Source Code:
Menu2View.cpp
---------------------------------------------------------------------------------
void CMenu2View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
if (0x0d == nChar)
{
if (0==++m_nIndex)
{
m_menu.CreatePopupMenu();
//添加弹出菜单
GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"PhoneBook");
//重画菜单栏,NOTE:菜单是与框架窗口相关,所以需要使用GetParent();
GetParent()->DrawMenuBar();
}
//添加菜单项
m_menu.AppendMenu(MF_STRING,IDM_PHONE1+m_nIndex,m_strLine.Left(m_strLine.Find(' ')));
m_strArray.Add(m_strLine);
m_strLine.Empty();
Invalidate();
}
else
{
m_strLine+=nChar;
dc.TextOut(0,0,m_strLine);
}
CView::OnChar(nChar, nRepCnt, nFlags);
}
void CMenu2View::OnPhone1()
{
// TODO: Add your command handler code here
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(0));
}
void CMenu2View::OnPhone2()
{
// TODO: Add your command handler code here
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(1));
}
void CMenu2View::OnPhone3()
{
// TODO: Add your command handler code here
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(2));
}
void CMenu2View::OnPhone4()
{
// TODO: Add your command handler code here
CClientDC dc(this);
dc.TextOut(0,0,m_strArray.GetAt(3));
}
-------------------------------------------------------------------------------------
MainFrm.cpp
--------------------------------------------------------------------------------------
//在框架窗口中截获由视类响应的命令消息,通过覆盖基类的OnCommand函数
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
int MenuCmdID=LOWORD(wParam);//获得命令消息ID
CMenu2View* pMenu2View=(CMenu2View*)GetActiveView();//获得视类的指针
if (MenuCmdID>=IDM_PHONE1 && MenuCmdID <IDM_PHONE1+pMenu2View->m_strArray.GetSize())
{
CClientDC dc(pMenu2View);
dc.TextOut(0,0,pMenu2View->m_strArray.GetAt(MenuCmdID-IDM_PHONE1));
//MessageBox("Test");
return TRUE;
}
return CFrameWnd::OnCommand(wParam, lParam);
}
--------------------------------------------------------------------------------------