第 6章菜单、工具栏和状态栏
6.1 菜单
6.2 工具栏
6.3 状态栏
6.4 交互对象的动态更新作业
6.1菜单对于菜单的显示都遵循下列一些规则:
若点击某菜单项会弹出一对话框,那么在该菜单项文本后有,…” 。
若某项菜单有子菜单,那么在该菜单项文本后有,?” 。
菜单项需要助记符,用括号将带下划线的字母括起来。助记符与 Alt构成一个组合键,当按住,Alt”键不放,再敲击该字母时,对应的菜单项就会被选中。
若某项菜单需要快捷键的支持,则一般将其列在相应菜单项文本之后。任何时候按下快捷键,相应的菜单命令都会被执行。 图 6.1 File菜单
6.1菜单
6.1.1用编辑器设计菜单
编辑菜单在菜单的空位置上双击鼠标左键,则出现它的属性对话框,如图。
图 6.2 菜单编辑器菜单的空位置
6.1菜单图 6.3 菜单属性对话框
6.1菜单需要注意的是:
当菜单项的属性中选中了 Pop_up时,对话框中 ID,Separator和 Prompt项无效。
增加新的菜单项后,用户可以用鼠标将菜单项拖到其他位置,而当菜单项位置改变时,其属性并没改变。
菜单项被拖动的位置图 6.4 拖动菜单项图 6.5 添加的菜单项
6.1菜单
菜单命令的消息映射
[例 Ex_Mmenu]“成绩输入,菜单项的映射。
(1)选 择,View”菜单?,ClassWizard”命令项或按 Ctrl+w快捷键,则出现
MFC ClassWizard对话框,切换到 Message Maps页面。
(2)从,Class name”列表中选择 CMainFrame,在 IDs列表中选择
ID_SCORE_IN,然后在 Messages框中选择 COMMAND消息,如图。
图 6.6 消息的选择
6.1菜单
(3)单击 [Add Function]按钮或双击 COMMAND消息,输入成员函数的名称。
如图。
(4)单击 [OK]。选择新增加的成员函数,单击 [Edit Code]按钮 (或直接在函数名双击鼠标左键 ),就可以在此成员函数中添加一些代码。例如:
void CMainFrame::OnScoreIn()
{ // TODO,Add your command handler code here
AfxMessageBox("现在就输入成绩吗? ");
}
(5)编译并运行。在应用程序的顶层菜单上,单击,成绩输入,菜单项或按
Alt+S组合键,如图。
图 6.7 添加成员函数对话框图 6.8 菜单命令执行的结果
6.1菜单
6.1.2使用键盘快捷键
(1)先打开上例的项目工作区窗口中 Accelerator的资源项,双击
IDR_MAINFRAME,如图。
图 6.9 示例 Ex_Sdi的加速键资源最下端的空行
6.1菜单
(2)要建立一新的加速键,双击加速键列表的最下端的空行,如图,其中可设置的属性如表
(3)在上述对话框中,选择在 Ex_Sdi示例中添加的,成绩输入,菜单项的 ID
号 ID_SCORE_IN作为要联用的加速键的 ID号,单击 [Next Key Typed],按下 Ctrl+1作为此加速键的键值。
图 6.10,Accel Properities”对话框
6.1菜单
6.1.3菜单的编程控制
菜单的基本操作
(1)创建菜单
CMenu类的 CreateMenu和 CreatePopupMenu用来创建一个菜单或子菜单框架,原型如下:
BOOL CreateMenu( ); // 产生一个空菜单
BOOL CreatePopupMenu( ); // 产生一个空的弹出式子菜单
(2)装入菜单将菜单从资源装入应用程序中,需调用 CMenu成员函数 LoadMenu,或者用 SetMenu
对应用程序菜单进行重新设置。
BOOL LoadMenu( LPCTSTR lpszResourceName );
BOOL LoadMenu( UINT nIDResource );
(3)添加菜单项菜单创建后,可调用 AppendMenu或 InsertMenu函数添加一些菜单项。 AppendMenu
是将菜单项添加在菜单的末尾处,InsertMenu在菜单的指定位置处插入菜单项,并将后面的菜单项依次下移。
BOOL AppendMenu( UINT nFlags,UINT nIDNewItem = 0,LPCTSTR lpszNewItem
= NULL );
BOOL AppendMenu( UINT nFlags,UINT nIDNewItem,const CBitmap* pBmp );
BOOL InsertMenu( UINT nPosition,UINT nFlags,UINT nIDNewItem = 0,
LPCTSTR lpszNewItem = NULL );
BOOL InsertMenu( UINT nPosition,UINT nFlags,UINT nIDNewItem,const
CBitmap* pBmp );
6.1菜单需要注意的是:
nFlags为 MF_BYPOSITION时,nPosition表示新菜单项要插入的具体位置,
为 0时表示第一个菜单项,为 -1时,将菜单项添加菜单的末尾处。
nFlags的标志中,可以用,|”(按位或 )来组合,例如
MF_CHECKED|MF_STRING等。有些组合是不允许的,例如 MF_DISABLED、
MF_ENABLED和 MF_GRAYED,MF_STRING,MF_OWNERDRAW、
MF_SEPARATOR和位图,MF_CHECKED和 MF_UNCHECKED 都不能组合在一起。
当菜单项增加后,不管菜单依附的窗口是否改变,都应调用
CWnd::DrawMenuBar来更新菜单。
6.1菜单
(4)删除菜单项调用 DeleteMenu函数可将指定的菜单项删除,需要注意的是:调用该函数后,不管菜单依附的窗口是否改变,都应调用 CWnd::DrawMenuBar使菜单更新。函数 DeleteMenu的原型如下:
BOOL DeleteMenu( UINT nPosition,UINT nFlags );
nPosition表示要删除的菜单项位置,它由 nFlags进行说明。若当 nFlags为
MF_BYCOMMAND时,nPosition表示菜单项的 ID号,而当 nFlags为
MF_BYPOSITION时,nPosition表示菜单项的位置 (第一个菜单项位置为 0)。
(5)获取菜单项下面的三个 CMenu成员函数分别获得菜单的项数、菜单项的 ID号以及弹出式子菜单的句柄。
UINT GetMenuItemCount( ) const;
该函数用来获得菜单的菜单项数,调用失败后返回 -1。
UINT GetMenuItemID( int nPos ) const;
该函数用来获得由 nPos指定菜单项位置 (以 0为基数 )的菜单项的标识号,若
nPos是 SEPARATOR,则返回 -1。
CMenu* GetSubMenu( int nPos ) const;
该函数获得指定菜单的弹出式菜单的菜单句柄。该弹出式菜单位置由参数
nPos指定,开始的位置为 0。若菜单不存在,则创建一个临时的菜单指针 。
6.1菜单
给系统菜单添加一个菜单项
[例 Ex_SysMenu] 向应用系统菜单中添加一个菜单项。
(1)打开一个单文档应用程序项目,若没有可按前面提及的方法进行创建。
(2)选择,View”菜单?,Resource Symbols…” 命令,如图。
(3)单击 [New],如图。在 Name框中,可以指定一个用于菜单项的 ID号。在
Value框中,可以定义该 ID的值,系统要求定义的 ID值应大于 15(0X000F)而小于 61440(0XF000)。选择默认的 ID值 101,单击 [OK]按钮。
图 6.11,Resource Symbols”对话框图 6.12,New Symbol”对话框
6.1菜单
(4)在 CMainFrame::OnCreate函数体 (或自定义的函数 )中加入代码:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{,..
CMenu* pSysMenu = GetMenu(); // 获得主菜单句柄
CMenu* pSubMenu = pSysMenu->GetSubMenu(1);// 获得第二个子菜单的句柄
CString StrMenuItem("新的菜单项 ");
pSubMenu->AppendMenu(MF_SEPARATOR); // 增加一水平分隔线
pSubMenu->AppendMenu(MF_STRING,ID_NEW_MENUITEM,StrMenuItem);
// 允许使用 ON_UPDATE_COMMAND_UI或 ON_COMMAND的菜单项
m_bAutoMenuEnable = FALSE; // 使添加的菜单处于允许状态,默认为灰显
pSysMenu-
>EnableMenuItem(ID_NEW_MENUITEM,MF_BYCOMMAND|MF_ENABLED);
DrawMenuBar(); // 更新菜单
return 0;
}
(5)使用 ClassWizard处理 OnCommand消息并检测用户菜单的 nID参数。
BOOL CMainFrame::OnCommand(WPARAM wParam,LPARAM lParam)
{ // wParam的低字节表示菜单、控件、加速键的命令 ID
if (LOWORD(wParam) == ID_NEW_MENUITEM)
MessageBox("你选中了新的菜单项 ");
return CFrameWnd::OnCommand(wParam,lParam);
}
(6)编译运行并测试。
6.1菜单
6.1.4使用快捷菜单用资源编辑器和 MFC库的 CMenu::TrackPopupMenu函数创建这样的菜单,
CMenu::TrackPopupMenu函数原型如下:
BOOL TrackPopupMenu( UINT nFlags,int x,int y,CWnd* pWnd,LPCRECT
lpRect = NULL );
x和 y表示菜单的水平坐标和菜单的顶端的垂直坐标。 pWnd表示弹出菜单的窗口。 lpRect是一个 RECT结构或 CRect对象指针,表示一个矩形区域,单击这个区域时,弹出菜单不消失。当 lpRect为 NULL时,若击在菜单外面,菜单立刻消失。
6.1菜单
[例 Ex_Menu]创建弹出菜单。
(1)选择,Insert”菜单?,Resource”命令或按 Ctrl+R快捷键,向应用程序项目添加一个新的菜单资源 (默认的 ID号为 IDR_MENU1)。
(2)用菜单编辑器,为该菜单资源中的顶层菜单的第一项加一任意标题 (实际上该标题无用 ),在此菜单项下依次添加如下表所示的菜单项。
(3)将此菜单资源 ID号改为 IDR_MYFLOATMENU。
(4)按 Ctrl+W快捷键打开 ClassWizard,将出现一对话框,询问是,选择一个已存在的类,,还是,创建一个新类,。选择,选择一个已存在的类,项并选定 CMainFrame类。
6.1菜单
(5)在 CMainFrame类中,选择上表中所列的菜单 ID,双击 COMMAND消息。这是仅为菜单 ID_MENU_SCOREIN映射 COMMAND消息,添加代码:
void CMainFrame::OnMenuScorein()
{ AfxMessageBox("现在就输入成绩吗? "); }
(6)在 CMainFrame类加入 WM_CONTEXTMENU消息处理函数,代码。
void CMainFrame::OnContextMenu(CWnd* pWnd,CPoint point)
{ CMenu menu;
menu.LoadMenu(IDR_MYFLOATMENU);
menu.GetSubMenu(0)
->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,
point.x,point.y,this);
}
(7)运行并测试。在应用程序的窗口客户区中单击鼠标右键,弹出如图菜单。
,成绩打印,菜单命令没有消息处理函数,因此它是灰显的,用户不能使用它们。若用户选中,成绩输入,菜单命令,则会执行相应的代码。
图 6.13 快捷菜单
6.2工具栏
6.2.1使用工具栏编辑器选择,File”?“Open Workspace”,将单文档应用程序 Ex_Sdi调入或重新创建。在项目工作区窗口中选择 ResourceView标签,双击,Toolbar”项中的
IDR_MAINFRAME,则工具栏编辑器出现在主界面的右边,如图。
图 6.14 工具栏编辑器窗口空按钮
6.2工具栏
(1)创建一个新的工具栏按钮在新建的工具栏中,最右端总有一个空按钮,双击该按钮弹出其属性对话框,
在 ID框中输入其标识符名称,则在其右端又出现一个新的空按钮。单击该按钮,在编辑器的视窗内进行编辑,这个编辑就是绘制一个按钮位图。
(2)移动一个按钮在工具栏中移动一个按钮,用鼠标左键点中它并拖动至相应位置即可。如果用户拖动它离开工具栏位置,则此按钮从工具栏中消失。若在移动一个按钮的同时,按下 Ctrl键,则在新位置复制一个按钮,新位置可以是同一个工具栏中的其他位置,也可以在不同的工具栏中。
(3)删除一个按钮前面已提到过,将选取中的按钮拖离工具栏,则该按钮就消失了。但若选中按钮后,单击 Delete键并不能删除一个按钮,只是将按钮中的图形全部以背景色填充。
(4)在工具栏中插入空格
如果按钮前没有任何空格,拖动该按钮向右移动并当覆盖相邻按钮的一半以上时,释放鼠标键,则此按钮前出现空格。
如果按钮前有空格而按钮后没有空格,拖动该按钮向左移动并当按钮的左边界接触到前面按钮时,释放鼠标键,则此按钮后将出现空格。
如果按钮前后均有空格,拖动该按钮向右移动并当接触到相邻按钮时,则此按钮前的空格保留,按钮后的空格消失。相反,拖动该按钮向左移动并当接触到前一个相邻按钮时,则此按钮前面的空格消失,后面的空格保留。
6.2工具栏
(5)工具栏按钮属性的设置双击某按钮弹出其属性对话框,如图。
图 6.15 工具栏按钮属性对话框
6.2工具栏
6.2.2工具栏和菜单相结合
[例 Ex_T&M] 将菜单项 ID_SCORE_IN与工具按钮相结合。
(1)将前面的单文档应用程序 Ex_Sdi打开;
(2)利用工具栏编辑器设计一个工具按钮,如图;
(3)双击刚才设计的钮工具按钮图形,弹出该工具按钮的属性对话框;
(4)在工具按钮的属性对话框中,将工具按钮的 ID号设为 ID_SCORE_IN,在 Prompt
框内键入,向系统输入学生成绩 \n成绩输入,;
(5)重新编译并运行程序。在 Ex_Sdi工具栏上,将鼠标指针移至刚才设计的工具按钮处,这时在状态栏上显示出,向系统输入学生成绩,信息,若稍等片刻后,还会弹出提示小窗口,显示出,成绩输入,字样。
图 6.16 工具栏按钮的设计
6.3状态栏
6.3.1状态栏的定义用 AppWizard创建的 SDI或 MDI应用程序框架中,有一个静态的 indicator数组,
它是在 MainFrm.cpp文件中定义的,被 MFC用作状态栏的定义。
Static UINT indicators[]=
{
ID_SEPARATOR,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
}
图 6.17 indicators数组的定义
6.3状态栏
6.3.2状态栏的常用操作
增加和减少窗格状态栏中的窗格可以分为信息行窗格和指示器窗格两类。若在状态栏中增加一个信息行窗格,则只需在 indicators数组中的适当位置中增加一个
ID_SEPARATOR标识;若在状态栏中增加一个用户指示器窗格,则在
indicators数组中的适当位置增加一个在字符串表中定义过的资源 ID,其字符串的长度表示用户指示器窗格的大小。若状态栏减少一个窗格,其操作与增加相类似,只需减少 indicators数组元素。
在状态栏上显示文本有三种办法可以在状态栏窗格显示文本信息:
(1)调用 CWnd::SetWindowText更新信息行窗格 (或窗格 0)中的文本。由于状态栏也是一种窗口,故在使用时可直接调用。若状态栏变量为
m_wndStatusBar,则 m_wndStatusBar,SetWindowText(“消息,)语句将在信息行窗格 (或窗格 0)内显示,消息,字样。
(2)手动处理状态栏的 ON_UPDATE_COMMAND_UI更新消息,并在处理函数中调用 CCmdUI::SetText函数。
(3)调用 CStatusBar::SetPaneText函数更新任何窗格 (包括信息行窗格 )中的文本。此函数原型描述如下:
BOOL SetPaneText( int nIndex,LPCTSTR lpszNewText,BOOL bUpdate =
TRUE );
6.3状态栏
[例 Ex_Status] 在状态栏的最右边两个窗格中显示出当前鼠标在窗口客户区的位置。
(1)将前面的单文档应用程序 Ex_Sdi打开或重新创建;
(2)将项目工作区窗口切换到 ResourceView页面,双击,String Table”项的
,String Table”图标。在字符串列表的最后一行空项上双击鼠标左键,如图:
(3)该对话框中,可以指定相应的 ID和字符串值,这里加入两个字符串资源
ID_LEFT和 ID_RIGHT,其字符的多少决定窗格的大小。如图:
图 6.17 字符串属性对话框添加的字符串图 6.18 添加的字符串资源
6.3状态栏
(4)打开 MainFrm.cpp文件,将原先的 indicators数组修改如下:
static UINT indicators[] =
{ ID_SEPARATOR,// 第一个信息行窗格
ID_SEPARATOR,// 第二个信息行窗格
ID_LEFT,// 第三个窗格
ID_RIGHT,// 第四个窗格
};
(5) 必须手工添加消息处理函数原型。打开 Ex_SdiView.h文件,在
AFX_MSG内增加消息处理语句,ClassWizard以后允许访问和编辑该代码。
protected:
//{{AFX_MSG(CEx_SdiView)
afx_msg void OnUpdateXY(CCmdUI* pCmdUI);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
(6)打开 Ex_SdiView.cpp文件,在其消息入口处添加消息映射函数:
BEGIN_MESSAGE_MAP(CEx_SdiView,CView)
//{{AFX_MSG_MAP(CEx_SdiView)
ON_UPDATE_COMMAND_UI(ID_LEFT,OnUpdateXY)
ON_UPDATE_COMMAND_UI(ID_RIGHT,OnUpdateXY)
//}}AFX_MSG_MAP
6.3状态栏
(7)增加修改状态栏指示器的消息映射函数代码,状态栏的窗格需要更新时,
应用程序框架自动调用此函数。
void CEx_SdiView::OnUpdateXY(CCmdUI* pCmdUI)
{ pCmdUI->Enable(TRUE); // 使窗格文本能被更新 }
(8)用 ClassWizard在 CEx_SdiView类中加入 WM_MOUSEMOVE(鼠标移动 )消息处理函数,并添加下列代码。该函数先获得状态栏对象的指针,然后调用
SetPaneText函数更新第三和第四窗格中的文本。
void CEx_SdiView::OnMouseMove(UINT nFlags,CPoint point)
{ CString str;
CMainFrame* pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd;
CStatusBar* pStatus=&pFrame->m_wndStatusBar;
if (pStatus)
{ str.Format("X=%d",point.x);// 格式化文本
pStatus->SetPaneText(2,str);
str.Format("Y=%d",point.y);
pStatus->SetPaneText(3,str);
}
CView::OnMouseMove(nFlags,point);
}
6.3状态栏
(9)将 MainFrm.h文件中的受保护变量 m_wndStatusBar变成公共变量。
(10)在 Ex_SdiView.cpp文件的开始处增加下列语句:
#include,MainFrm.h”
(11)编译并运行,如图。
图 6.19 鼠标的位置显示在状态栏上
6.3状态栏
改变状态栏的风格在 MFC的 CStatusBar类中,有两个成员函数可以改变状态栏风格,它们是:
void SetPaneInfo( int nIndex,UINT nID,UINT nStyle,int cxWidth );
void SetPaneStyle( int nIndex,UINT nStyle );
参数 nIndex表示要设置的状态栏窗格的索引,nID用来为状态栏窗格指定新的 ID,cxWidth表示窗格的像素宽度,nStyle表示窗格的风格类型,用来指定窗格的外观,例如 SBPS_POPOUT表示窗格是凸起来的,见表。
6.4交互对象的动态更新为能使交互对象动态更新,MFC通过 ClassWizard直接映射交互对象的更新命令消息来实现。自动将用户交互对象的 ID号与 ON_UPDATE_COMMAND_UI宏相连接并产生处理更新消息的相应函数。例如代码:
void CEx_SdiView::OnUpdateXY(CCmdUI* pCmdUI)
{ pCmdUI->Enable(TRUE); // 使窗格文本能被更新
}
OnUpdateXY就是映射更新命令消息的消息函数。此消息处理函数只有一个参数,它是指向 CCmdUI对象的指针。 CCmdUI 类仅用于
ON_UPDATE_COMMAND_UI处理函数,它的成员函数将对菜单项、工具按钮等用户交互对象起作用,如表。
6.4交互对象的动态更新
[例 Ex_T&Mitem]菜单项和工具按钮的更新。
(1)将上例的单文档应用程序项目 Ex_Sdi调入或重新创建。
(2)将项目工作区窗口切换到 ResourceView页面,选中 Toolbar资源
IDR_MAINFRAME,然后按下 Ctrl键不放,在资源 IDR_MAINFRAME上单击鼠标左击后向下拖一下,这时该工具条资源就被复制,新的资源号为
IDR_MAINFRAME1,将其改为 IDR_NEWBAR。
(3)打开 MainFrm.h文件,在 CMainFrame类中声明一个 CToolBar类变量
m_wndNewBar。
6.4交互对象的动态更新
(4)在 CMainFrame::OnCreate中添加下列代码:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{ if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
if (!m_wndToolBar.CreateEx(this,TBSTYLE_FLAT,WS_CHILD | WS_VISIBLE |
CBRS_TOP| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY |
CBRS_SIZE_DYNAMIC) ||!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{ TRACE0("Failed to create toolbar\n");
return -1; // fail to create }
if (!m_wndNewBar.CreateEx(this,TBSTYLE_FLAT,WS_CHILD | WS_VISIBLE |
CBRS_TOP| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY |
CBRS_SIZE_DYNAMIC) ||!m_wndNewBar.LoadToolBar(IDR_NEWBAR))
{ TRACE0("Failed to create newbar\n");
return -1; // fail to create }
……
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
m_wndNewBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
DockControlBar(&m_wndNewBar);
……
return 0;
}
6.4交互对象的动态更新
(5)打开菜单资源 IDR_MAINFRAME,在,查看,菜单下添加一个菜单项,新工具栏 (&N)”,ID号设定为 ID_VIEW_NEWBAR。
(6)打开 Toolbar资源 IDR_MAINFRAME,将上例新增的工具按钮同
ID_VIEW_NEWBAR菜单关联起来。
(7)为 CMainFrame类添加一个 BOOL型的成员变量 m_bNewBar。
(8)用 ClassWizard在 CMainFrame类中添加菜单 ID_VIEW_NEWBAR的
COMMAND和 UPDATE_COMMAND_UI两个消息处理函数,并添加下列代码:
void CMainFrame::OnViewNewbar()
{ m_bNewBar = !m_bNewBar;
ShowControlBar( &m_wndNewBar,m_bNewBar,FALSE);
}
void CMainFrame::OnUpdateViewNewbar(CCmdUI* pCmdUI)
{ m_bNewBar = m_wndNewBar.IsWindowVisible();
pCmdUI ->SetCheck(m_bNewBar);
}
(9)编译运行并测试。打开,查看,菜单,可以看到,新工具栏,菜单前面有一个,√”,同时有一个工具按钮被选中,单击该工具按钮或选择,新工具栏,菜单,则新创建的工具栏不见,同时这个工具按钮恢复原状,而,新工具栏,菜单前面没有任何标记。若将代码中的 SetCheck改为 SetRadio,
则,√” 变成了,?”,这就是交互对象的更新效果。
作业
1.p376:8,11
2.p428:实验 11