58
第 6 第第第第第第第第第 6.1 第第第第 6.2 第第 6.3 第第第 6.4 第第第 6.5 第第第第第第第第第 6.6 第第第第第

第 6 章框架窗口界面设计

  • Upload
    reuel

  • View
    105

  • Download
    5

Embed Size (px)

DESCRIPTION

第 6 章框架窗口界面设计. 6.1 框架窗口 6.2 菜单 6.3 工具栏 6.4 状态栏 6.5 交互对象的动态更新 6.6 图标和光标. 6.1 框架窗口. 6.1.1 单文档和多文档程序框架窗口 文档应用程序的 MFC 类结构 如图是单文档应用程序 Ex_SDI 和多文档应用程序 Ex_MDI 的 MFC 类结构。. CAboutDlg 是每一个应用程序框架都有的,用来显示本程序的有关信息。 CEx_SDIApp 和 CEx_MDIApp 是应用程序类,是从 CWinApp 类派生而来,负责应用程序创建、运行和终止,每一个应用程序都需要这样的类。 - PowerPoint PPT Presentation

Citation preview

Page 1: 第 6 章框架窗口界面设计

第 6章框架窗口界面设计 6.1 框架窗口 6.2 菜单 6.3 工具栏 6.4 状态栏 6.5 交互对象的动态更新 6.6 图标和光标

Page 2: 第 6 章框架窗口界面设计

6.1 框架窗口 6.1.1单文档和多文档程序框架窗口 文档应用程序的 MFC 类结构

如图是单文档应用程序 Ex_SDI 和多文档应用程序 Ex_MDI 的 MFC 类结构。

CAboutDlg 是每一个应用程序框架都有的,用来显示本程序的有关信息。CEx_SDIApp 和 CEx_MDIApp 是应用程序类,是从 CWinApp 类派生而来,负责应用程序创建、运行和终止,每一个应用程序都需要这样的类。CEx_SDIDoc 和 CEx_MDIDoc 是文档类,它们是从 CDocument 类派生而来,负责应用程序文档数据管理。CEx_SDIView 和 CEx_MDIView 是视图类,它们既可以从基类 CView 派生,也可以从 CView 派生类派生,负责数据的显示、绘制和其他用户交互。CMainFrame 类是用来负责主框架窗口的显示和管理,包括工具栏和状态栏等界面元素的初始化。对于单文档应用程序来说,主框架窗口类是从 CFrameWnd 派生而来的,而对于多文档应用程序程序,主框架窗口是从 CMDIFrameWnd 派生的。

Page 3: 第 6 章框架窗口界面设计

6.1 框架窗口CChildFrame 类是从 CMDIChildWnd 派生而来,专门用来管理多文档应用程序中的文档窗口。无论是 CMDIChildWnd 还是 CMDIFrameWnd 类,它们都是从 CFrameWnd 派生而来的。对于基于对话框的应用程序,一般有 CAboutDlg 类、应用程序类和对话框类。上述 MFC 类之间的继承和派生关系如图 6.2 所示。

图 6.2 MFC 类的基本层次结构

CCmdTarget

CWnd

CDocument

CFrameWnd

CObject

CWinTread

CWinApp

CView CDialog 及控件

CMDIChildWndCMDIFrameWnd CMiniFrameWnd

Page 4: 第 6 章框架窗口界面设计

6.1 框架窗口 项目的文件组织 项目中源文件都是采用文件夹的方式进行管理的,将项目名作为文件夹名,此文件夹下包含源程序代码文件、项目文件以及项目工作区文件等。每一个类的源代码均保存同名的 h 和 cpp 文件。其他一些文件类型的含义:.opt 关于开发环境的参数文件,如工具条位置等信息 .aps(AppStudio File) 资源辅助文件,二进制格式.clw ClassWizard 信息文件.dsp(DeveloperStudio Project) 项目文件.plg 编译信息文件.mdp(Microsoft DevStudio Project) 旧版本的项目文件.bsc 用于浏览项目信息.map 执行文件的映像信息纪录文件.pch(Pre-Compiled File) 预编译文件,可以加快编译速度,但是文件非常大.pdb(Program Database) 记录程序有关的一些数据和调试信息.ncb 无编译浏览文件 (no compile browser) 还有相应的 Debug( 调试 ) 、 Res( 资源 ) 等子文件夹。用应用程序向导创建项目时,系统自动为项目创建 Win32 Debug 版本的运行程序,并使用相应的默认配置。程序编连后,用于该项目的可执行文件以及其他调试的信息文件都安放在 Debug 文件夹中。 程序项目有一个 Win32 Release 版本,与 Release 的区别在于: Debug 版本的运行程序有相应的调试信息码, Release 版本的运行程序没有,但 Release 版本的运行程序经过代码的优化,其程序的运行效率被最大提升。 选择“编译”“配置”菜单命令,在“配置”对话框将项目设置成Win32 Release 版本。

Page 5: 第 6 章框架窗口界面设计

6.1 框架窗口 主框架窗口和文档窗口 主框架窗口是直接放置在桌面 (DeskTop) 上的那个窗口,每个应用程序只能有一个主框架窗口。主框架窗口负责管理各个用户交互对象并根据用户操作相应地创建或更新文档窗口及其视图。 文档窗口对 SDI 程序来说,和主框架窗口是一致的,主框架窗口就是文档窗口;对于 MDI 程序,文档窗口是主框架窗口的子窗口,如图。 文档窗口一般都有相应的可见边框,它的客户区 (除了窗口标题栏、边框外的区域 ) 是由相应的视图来构成的,因此可以说视图是文档窗口内的子窗口。文档窗口时刻跟踪当前处于活动状态的视图的变化,并将用户或系统产生的消息传递给当前活动视图。

Page 6: 第 6 章框架窗口界面设计

6.1 框架窗口6.1.2窗口状态的改变

MFC AppWizard 为每个窗口设置了相应的大小和位置。运行程序时,会自动调用框架内部的 WinMain函数,并自动查找该应用程序类的全局变量 theApp ,然后自动调用用户应用程序类的虚函数 InitInstance ,该函数会进一步调用相应的函数来完成主窗口的构造和显示工作,代码:BOOL CEx_SDIApp::InitInstance(){ …

m_pMainWnd->ShowWindow(SW_SHOW); // 显示窗口m_pMainWnd->UpdateWindow(); // 更新窗口return TRUE;

} m_pMainWnd 是主框架窗口指针变量, ShowWindow 是 CWnd 类的成员函数,用来按指定的参数显示窗口。

Page 7: 第 6 章框架窗口界面设计

6.1 框架窗口

通过指定 ShowWindow函数的参数值可以改变改变窗口显示状态。例如下面的代码是将窗口的初始状态设置为“最小化”:BOOL CEx_SDIApp::InitInstance(){ ...

m_pMainWnd->ShowWindow(SW_SHOWMINIMIZED);m_pMainWnd->UpdateWindow();return TRUE;

}

Page 8: 第 6 章框架窗口界面设计

6.1 框架窗口6.1.3窗口风格的设置 窗口风格 通常有一般和扩展两种形式。可在函数 CWnd::Create或 CWnd::CreateEx参数中指定, CreateEx函数可同时支持以上两种风格, CWnd::Create只能指定窗口的一般风格。控件和对话框的窗口风格可直接通过其属性对话框来设置。 除了上述风格外,框架窗口还有以下三个自己的风格。它们都可以在 PreCreateWindow重载函数的 CREATESTRUCT结构中指定。 FWS_ADDTOTITLE

该风格指定相关的信息如文档名添加到框架窗口标题的后面。 FWS_PREFIXTITLE 该风格使得框架窗口标题中的文档名显示在应用程序名之前。 FWS_SNAPTOBARS 该风格用来调整窗口的大小,使它刚好包含了框架窗口中的控制栏。

Page 9: 第 6 章框架窗口界面设计

6.1 框架窗口 在 MFC AppWizard 中进行修改 [例 Ex_SDI] 在向导中修改窗口风格。 (1) 选择“文件”“新建”,选择“工程”,选择 MFC AppWizard(exe) 的项目类型,指定项目工作文件夹位置,输入项目名 Ex_SDI ,单击 [确定 ]按钮。(2)    在向导的第一步中,将应用程序类型选为“单个文档” (SDI) 。(3) 单击 [下一个 ],如图,可以选择程序中是否加入数据库的支持。(4) 单击 [下一个 ],出现向导的第三步对话框,如图,允许在程序中加入复合文档、自动化、 ActiveX控件的支持。

Page 10: 第 6 章框架窗口界面设计

6.1 框架窗口(5) 单击 [下一个 ],如图,对话框的前几项依次确定对浮动工具条、打印与预览以及通信网络等特性的支持。对话框的最后两项是最近文件列表数目的设置 ( 默认时为 4) 和一个 [ 高级 ] 按钮 。

Page 11: 第 6 章框架窗口界面设计

6.1 框架窗口(6) 在第四步对话框中,单击 [高级 ],如图,分别用于文档模板字串资源内容和窗口风格的修改。

Page 12: 第 6 章框架窗口界面设计

6.1 框架窗口(7) 单击 [关闭 ],单击 [下一个 ],如图。可选择:应用程序的主窗口是 MFC标准风格还是窗口左边有切分窗口的浏览器风格,在源文件中是否加入注释用来引导用户编写程序代码,是使用动态链接库 (DLL) 还是静态链接库。(8) 单击 [下一个 ] 按钮,出现如图 6.9所示的对话框。在这里,用户可以对MFC AppWizard 提供的默认类名、基类名、各个源文件名进行修改。

Page 13: 第 6 章框架窗口界面设计

6.1 框架窗口(9)  单击 [完成 ],单击 [确定 ],回到主界面。在向导中作出的选择,有部分内容反映在 CEx_SDIApp::InitInstance函数中,代码:BOOL CEx_SDIApp::InitInstance(){// 若用户在 MFC AppWizard 的第三步中,选中了“ ActiveX Controls” ,// 则表示所创建的应用程序支持 ActiveX控件AfxEnableControlContainer(); // 若用户在 MFC AppWizard 的第四步中,选中了“ 3D controls”// 则表示所创建的应用程序支持 3D 控件风格#ifdef _AFXDLLEnable3dControls(); // 使用 MFC 动态链接库的 3D 控件#elseEnable3dControlsStatic(); // 使用 MFC静态链接库的 3D 控件#endif// 在 Windows 系统注册表中登记应用程序的主键值,以便将一些与应用程序// 相关的参数存放在该主键值下SetRegistryKey(_T("Local MFC AppWizard-Generated Applications"));// 从注册表中调入应用程序的一些标准参数值,其中包括最近文件列表的文件名LoadStdProfileSettings(); … return TRUE;}

Page 14: 第 6 章框架窗口界面设计

6.1 框架窗口 修改 CREATESTRUCT 结构

窗口创建前,自动调用 PreCreateWindow虚函数。用 MFC AppWizard 创建文档应用程序结构时, MFC已为主窗口或文档窗口类自动重载了该虚函数。可以在此函数中通过修改 CREATESTRUCT结构来设置窗口的绝大多数风格。对于多文档应用程序,文档窗口的风格可用下列的代码更改:BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs) { // 创建不含有 [最大化 ] 按钮的子窗口cs.style &= ~WS_MAXIMIZEBOX; // 调用基类的 PreCreateWindow函数return CMDIChildWnd::PreCreateWindow(cs); }

Page 15: 第 6 章框架窗口界面设计

6.1 框架窗口 使用 ModifyStyle 和 ModifyStyleEx ModifyStyleEx 可更改窗口的扩展风格。两个函数具有相同的参数,含义。BOOL ModifyXXXX( DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0 );参数 dwRemove 指定需要删除的风格, dwAdd指定需要增加的风格, nFlags表示SetWindowPos 的标志。 框架窗口设定扩展风格只能通过调用 ModifyStyle函数来进行。例如用 MFC ClassWizard 为一个多文档应用程序 Ex_MDI 的子文档窗口类 CChildFrame添加 OnCreateClient消息处理,并增加下列代码:BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { ModifyStyle(0, WS_VSCROLL, 0);return CMDIChildWnd::OnCreateClient(lpcs, pContext); }

Page 16: 第 6 章框架窗口界面设计

6.1 框架窗口6.1.4改变窗口的大小和位置CWnd 类的成员函数 SetWindowPos或 MoveWindow 可以改变窗口的大小和位置。SetWindowPos 可以改变窗口的大小、位置,还可以改变所有窗口在堆栈排列的次序(Z次序 ) ,这个次序是根据它们在屏幕出现的先后来确定的。BOOL SetWindowPos( const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags );参数 pWndInsertAfter 表示窗口对象指针,下列预定义窗口对象的地址:wndBottom 将窗口放置在 Z次序中的底层wndTop 将窗口放置在 Z次序中的顶层wndTopMost 设置最顶窗口wndNoTopMost 将窗口放置在所有最顶层的后面,若此窗口不是最顶窗口,则此标志无效。

x 和 y表示窗口新的左上角坐标, cx 和 cy分别表示表示窗口新的宽度和高度, nFlags表示窗口新的大小和位置方式。

Page 17: 第 6 章框架窗口界面设计

6.1 框架窗口 函数 CWnd::MoveWindow 也可用来改变窗口的大小和位置,必须在 MoveWindow函数中指定窗口的大小。void MoveWindow( int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE );void MoveWindow( LPCRECT lpRect, BOOL bRepaint = TRUE );参数 x 和 y 表示 窗口新的左上角坐标, nWidth 和 nHeight表示窗口新的宽度和高度, bRepaint 用于指定窗口是否重绘, lpRect表示窗口新的大小和位置。 例如,在 CEx_SDIApp::InitInstance 中 [return TRUE] 语句之前添加下列代码,可以使主框架窗口移动到屏幕的 (100, 100)处:// 若使用 SetWindowPos函数,则可以有m_pMainWnd->SetWindowPos(NULL,100,100,0,0,SWP_NOSIZE|SWP_NOZORDER);// 或使用 MoveWindow函数,则有CRect rcWindow;m_pMainWnd->GetWindowRect(rcWindow);m_pMainWnd->MoveWindow(100,100,rcWindow.Width(),rcWindow.Height(),TRUE);CRect 是一个矩形类, GetWindowRect 是一个 CWnd 类的成员函数,用来获取窗口在屏幕的位置和大小。

Page 18: 第 6 章框架窗口界面设计

6.2菜单 许多程序员对于菜单的设计都遵循下列一些规则: 若选择某菜单项会弹出一对话框,那么在该菜单项文本后有“…”。 若某项菜单有子菜单,那么在该菜单项文本后有“”。 若菜单项需要助记符,用括号将带下划线的字母括起来。助记符与 Alt 构成一个组合键,当按住“ Alt”键不放,敲击该字母时,对应的菜单项就会被选中。若某项菜单需要快捷键的支持,则一般将其列在相应菜单项文本之后。所谓“快捷键”是一个组合键,如 Ctrl+N,使用时是先按下“ Ctrl”健不放,然后再按“ N”键。任何时候按下快捷键,相应的菜单命令都会被执行。

图 6.11 菜单样例子

Page 19: 第 6 章框架窗口界面设计

6.2菜单6.2.1更改应用程序菜单

菜单“文件”“打开工作区”。在工作区中选择 ResourceView页面,双击资源“ Menu” 中的 IDR_MAINFRAME ,通过菜单编辑器修改菜单资源 IDR_MAINFRAME ,可以为程序重新指定一个菜单,下面的示例过程:(1)   打开前面的单文档应用程序 Ex_SDI 。(2) 按快捷键 Ctrl+R ,在资源类型中选定“ Menu” ,如图。 (3) 单击 [新建 ],如图。

Page 20: 第 6 章框架窗口界面设计

6.2菜单(4) 在 Menu 的 ID_MENU1上右击鼠标,选择“ Properties” 命令,如图,可以重新指定菜单资源 ID ,设置菜单资源的语言和条件,这个条件用来决定菜单资源包含到哪个环境中,例如当指定条件为 _DEBUG,则菜单资源只存在于 Debug 编译环境中。 (5) 菜单 ID_MENU1添加一个顶层弹出菜单项“测试 (&T)” ,并在该菜单下添加一个子菜单项“返回 (&R)” , ID 设为 ID_TEST_RETURN,如图。 (6)打开 Ex_SDI 程序菜单资源 IDR_MAINFRAME ,在“查看”菜单的最后添加一个子菜单项“显示测试菜单 (&M)” , ID 设为 ID_VIEW_TEST。(7) 为 CMainFrame 类添加一个 CMenu 类型的成员变量m_NewMenu , CMenu 类是用来处理菜单的一个 MFC 类。

Page 21: 第 6 章框架窗口界面设计

6.2菜单(8)按快捷键 Ctrl+W ,切换到 Message Maps页面,从“ Class name”列表中选择 CMainFrame ,分别为菜单项 ID_VIEW_TEST 和 ID_TEST_RETURN 添加 COMMAND消息映射,使用默认的消息映射函数名,并添中下列代码:void CMainFrame::OnViewTest() { m_NewMenu.Detach(); // 使菜单对象和菜单句柄分离m_NewMenu.LoadMenu( IDR_MENU1 ); SetMenu(NULL); // 清除应用程序菜单SetMenu( &m_NewMenu ); // 设置应用程序菜单}void CMainFrame::OnTestReturn() { m_NewMenu.Detach();m_NewMenu.LoadMenu( IDR_MAINFRAME );SetMenu(NULL);SetMenu( &m_NewMenu ); } LoadMenu装载菜单资源, Detach 使菜单对象与菜单句柄分离。调用 LoadMenu 后,菜单对象m_NewMenu就拥有一个菜单句柄,再次调用 LoadMenu时,发生运行时错误,当菜单对象与菜单句柄分离后,就可以再创建菜单。 SetMenu 是 CWnd 类的一个成员函数,用来设置应用程序的菜单。(9) 编译运行并测试。

Page 22: 第 6 章框架窗口界面设计

6.2菜单6.2.2使用键盘快捷键 通过上述的菜单系统,可以选择几乎所有可用的命令和选项,保证了菜单命令系统的完整性,菜单系统操作效率不高。对于那些反复使用的命令,有必要进一步提高效率,于是加速键应运而生。 一个加速键就是一个按键或几个按键的组合,用于激活特定的命令。加速键也是一种资源。 例如:为前面两个菜单 ID_VIEW_TEST和 ID_TEST_RETURN 定义键盘快捷键:

(1)打开上例的工作区窗口中 Accelerator 的资源项,双击 IDR_MAINFRAME ,如图。

Page 23: 第 6 章框架窗口界面设计

6.2菜单(2) 建立一新的加速键,双击加速键列表的最下端的空行,弹出如图的“ Accel Properities”( 加速键属性 ) 对话框,可设置的属性如表。

Page 24: 第 6 章框架窗口界面设计

6.2菜单(3) 选择 Ex_SDI添加的“显示测试菜单”菜单项 ID_VIEW_TEST作为要联用的加速键的 ID号,然后单击 [下一键 ]按钮,并按下 Ctrl+1作为此加速键的键值。(4)按同样的方法,为菜单项 ID_TEST_RETURN添加加速键 Ctrl+2 。需要说明的是,为了使其他用户能查看并使用该加速键,还需在相应的菜单项文本后面添加加速键内容。例如,可将 ID_VIEW_TEST菜单项的标题改成“显示测试菜单 (&M)\tCtrl+1” ,其中“ \t” 是将后面的“ Ctrl+1”定位到一个水平制表位。(5) 编译运行并测试。当程序运行后,按“ Ctrl+1” 和“ Ctrl+2” 将执行相应的菜单命令。

Page 25: 第 6 章框架窗口界面设计

6.2菜单6.2.3菜单的编程控制 创建菜单 CreateMenu 和 CreatePopupMenu 用来创建一个菜单或子菜单框架,原型:BOOL CreateMenu( ); // 产生一个空菜单BOOL CreatePopupMenu( ); // 产生一个空的弹出式子菜单 装入菜单资源 需调用 CMenu成员函数 LoadMenu ,再用 SetMenu 对应用程序菜单进行重新设置。BOOL LoadMenu( LPCTSTR lpszResourceName );BOOL LoadMenu( UINT nIDResource );lpszResourceName 为菜单资源名称, nIDResource 为菜单资源 ID号。 添加菜单项 可以调用 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 );nIDNewItem表示新菜单项的 ID号, lpszNewItem表示新菜单项的内容, pBmp 用于菜单项的位图指针, nPosition表示新菜单项要插入的菜单项位置。 nFlags表示要增加的新菜单项的状态信息。

Page 26: 第 6 章框架窗口界面设计

6.2菜单注意:

 当 nFlags 为 MF_BYPOSITION时, nPosition表示新菜单项要插入的具体位置,为 0时表示第一个菜单项,为 -1时,将菜单项添加菜单的末尾处。  nFlags 的标志中,可以用“ |”(按位或 ) 来组合。有些组合是不允许的,例如 MF_DISABLED 、 MF_ENABLED 和 MF_GRAYED , MF_STRING、 MF_OWNERDRAW 、 MF_SEPARATOR 和位图, MF_CHECKED 和 MF_UNCHECKED 都不能组合在一起。当菜单项增加后,不管菜单依附的窗口是否改变,都应调用 CWnd::DrawMenuBar 来更新菜单。

Page 27: 第 6 章框架窗口界面设计

6.2菜单 删除菜单项 DeleteMenu函数可将菜单项删除,注意:调用该函数后,不管菜单依附的窗口是否改变,都应调用 CWnd::DrawMenuBar 使菜单更新。函数原型:BOOL DeleteMenu( UINT nPosition, UINT nFlags );nPosition表示要删除菜单项位置,由 nFlags 进行说明。 nFlags 为 MF_BYCOMMAND 时, nPosition表示菜单项 ID号, nFlags 为 MF_BYPOSITION时, nPosition表示菜单项的位置。 获取菜单项 下面四个 CMenu成员函数分别获得菜单的项数、菜单项的 ID号、菜单项的文本内容以及弹出式子菜单的句柄。UINT GetMenuItemCount( ) const;// 用来获得菜单的菜单项数。UINT GetMenuItemID( int nPos ) const;// 用来获得由 nPos指定菜单项位置的菜单项的标识号。int GetMenuString( UINT nIDItem, CString& rString, UINT nFlags ) const; //获得由 nIDItem指定菜单项位置的菜单项的文本内容,由 rString 参数返回, nFlags 为 MF_BYPOSITION时, nPosition表示菜单项的位置。CMenu* GetSubMenu( int nPos ) const; // 用来获得指定菜单的弹出式菜单的菜单句柄。

Page 28: 第 6 章框架窗口界面设计

6.2菜单 示例 示例过程利用 CMenu成员函数向应用程序菜单中添加并处理一个菜单项:(1)   打开前面的单文档应用程序 Ex_SDI或重新创建。(2) “查看”菜单“ Resource Symbols…” ,如图。(3) 单击 [ 新建 ],如图。在名字框中输入标识符 ID_NEW_MENUITEM 。在值框中输入 ID 的值,单击 [确定 ]。

Page 29: 第 6 章框架窗口界面设计

6.2菜单(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;}

Page 30: 第 6 章框架窗口界面设计

6.2菜单(5)  用 MFC ClassWizard处理 OnCommand消息并检测用户菜单的 nID 参数。BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam) {

// wParam 的低字节表示菜单、控件、加速键的命令 IDif (LOWORD(wParam) == ID_NEW_MENUITEM) MessageBox("你选中了新的菜单项 ");

return CFrameWnd::OnCommand(wParam, lParam);}

(6) 编译运行并测试。这样当选择菜单“编辑”“新的菜单项”命令后,就会弹一个对话框,显示“你选中了新的菜单项”消息。

Page 31: 第 6 章框架窗口界面设计

6.2菜单6.2.4使用快捷菜单 快捷菜单实现函数

用资源编辑器和 MFC库的 CMenu::TrackPopupMenu函数可以创建这样的菜单, CMenu::TrackPopupMenu函数原型:BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );用来显示一个浮动的弹出式菜单。 nFlags表示菜单在屏幕显示的位置以及鼠标按钮标志,如表。 x 和 y 表示菜单的水平坐标和菜单的顶端的垂直坐标。pWnd表示弹出菜单的窗口,此窗口将收到菜单全部的 WM_COMMAND消息。 lpRect 是一个 RECT结构或 CRect 对象指针,表示一个矩形区域,单击这个区域时,弹出菜单不消失。 lpRect 为 NULL时,在菜单外面单击鼠标,菜单立刻消失。

Page 32: 第 6 章框架窗口界面设计

6.2菜单 示例 显示主菜单 IDR_MAINFRAME 时,右击鼠标弹出“查看”菜单的子菜单,显示菜单 IDR_MENU1时,右击鼠标弹出“测试”菜单的子菜单。具体步骤:(1)  打开前面的单文档应用程序 Ex_SDI 。(2)  在 CMainFrame 类添加 WM_CONTEXTMENU消息映射,函数代码。void CMainFrame::OnContextMenu(CWnd* pWnd, CPoint point) { CMenu* pSysMenu = GetMenu(); // 获得程序菜单指针int nCount = pSysMenu->GetMenuItemCount(); // 获得顶层菜单个数int nSubMenuPos = -1;for (int i=0; i<nCount; i++) // 查找菜单{ CString str;pSysMenu->GetMenuString(i, str, MF_BYPOSITION);if ((str.Left(4) == “查看” ) || (str.Left(4) == “测试” )) { nSubMenuPos = i;break; }}if (nSubMenuPos<0) return; // 没有找到,返回pSysMenu->GetSubMenu( nSubMenuPos) ->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, point.x, point.y, this);}(3) 编译运行并测试。

Page 33: 第 6 章框架窗口界面设计

6.3 工具栏 6.3.1使用工具栏编辑器 创建一个新的工具栏按钮 双击新建的工具栏中最右端的空按钮弹出属性框,在 ID 框中输入名称,右端出现一个空按钮。单击该按钮,在资源编辑器的工具按钮设计窗口内进行编辑,这个编辑就是绘制一个工具按钮的位图。

移动一个按钮鼠标左键点中它并拖动至相应位置。如果用户拖动它离开工具栏位置,则此按钮从工具栏中消失。若在移动一个按钮的同时,按下 Ctrl键,则在新位置复制一个按钮,新位置可以是同一个工具栏中的其他位置,也可以在不同的工具栏中。

删除一个按钮 将选取中的按钮拖离工具栏就消失了。若选中按钮, Delete键不能删除,将按钮中的图形全部以背景色填充。

在工具栏中插入空格 如果按扭前没有任何空格,拖动该按钮向右移动并当覆盖相邻按钮的一半以上时,释放鼠标键,则此按钮前出现空格。 如果按钮前有空格而按钮后没有空格,拖动该按钮向左移动并当按钮的左边界接触到前面按钮时,释放鼠标键,则此按钮后将出现空格。 如果按钮前后均有空格,拖动该按钮向右移动并当接触到相邻按钮时,则此按钮前的空格保留,按钮后的空格消失。相反,拖动该按钮向左移动并当接触到前一个相邻按钮时,则此按钮前面的空格消失,后面的空格保留。

Page 34: 第 6 章框架窗口界面设计

6.3 工具栏 工具栏按钮属性的设置

Page 35: 第 6 章框架窗口界面设计

6.3 工具栏6.3.2工具按钮和菜单项相结合 指选择工具按钮或菜单命令时,操作结果是一样的。使它们结合的具体方法是在工具按钮的属性对话框中将按钮的 ID号设置为相关联的菜单项 ID 。例如 : 示例是在前面 Ex_SDI 上进行的,通过两个工具按钮分别显示主菜单 IDR_MAINFRAME 和菜单 IDR_MENU1。具体步骤:

(1)  打开前面的单文档应用程序 Ex_SDI 。(2) 利用工具栏编辑器设计两个工具按钮,如图。

Page 36: 第 6 章框架窗口界面设计

6.3 工具栏(3) 双击设计的第一个工具按钮,将工具按钮的 ID号设为 ID_TEST_RETURN,在提示框内键入“返回应用程序主菜单 \n返回主菜单”。(4) 双击设计的第二个工具按钮,将工具按钮的 ID号设为 ID_VEW_TEST,在提示框内键入“显示测试菜单 \n 显示测试菜单”。(5) 编译运行并测试。将鼠标移至设计的第一个工具按钮处,这时在状态栏上显示出“返回应用程序主菜单”信息,若稍等片刻后,还会弹出提示小窗口,显示出“返回主菜单”字样,如图。单击新添加的这两个按钮,会执行相应的菜单命令。

Page 37: 第 6 章框架窗口界面设计

6.3 工具栏6.3.3多个工具栏的使用操作步骤如下:(1)   打开前面的单文档应用程序 Ex_SDI 。(2)   将工作区切换到 ResourceView ,展开 Toolbar( 工具栏 ) 资源,用鼠标单击 IDR_MAINFRAME不松开,按 Ctrl ,将 IDR_MAINFRAME拖到 Toolbar资源名称上。(3)右击工具栏资源 IDR_MAINFRAME1 ,选择 Properties 命令,如图。(4)双击 IDR_TOOLBAR1,按图删除不要的工具按钮。(5) 在 CMainFrame 类中添加一个成员变量m_wndTestBar ,变量类型为 CToolBar 。 CToolBar 类封装了工具栏的操作。

Page 38: 第 6 章框架窗口界面设计

6.3 工具栏(6)    在 CMainFrame::OnCreate函数中添加下面的工具栏创建代码:int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){ if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;int nRes = m_wndTestBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC,CRect(0,0,0,0),AFX_IDW_TOOLBAR + 10);if (!nRes || !m_wndTestBar.LoadToolBar(IDR_TOOLBAR1)){ TRACE0("Failed to create toolbar\n");return -1; // fail to create }…m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);m_wndTestBar.EnableDocking(CBRS_ALIGN_ANY);EnableDocking(CBRS_ALIGN_ANY);DockControlBar(&m_wndToolBar);DockControlBar(&m_wndTestBar);…return 0;}

Page 39: 第 6 章框架窗口界面设计

6.3 工具栏(7) 编译运行,如图。 (8) 需要调用 CFrameWnd 类的成员函数 ShowControlBar 来使程序一开始运行时隐藏工具栏 IDR_TOOLBAR1。在 CMainFrame::OnCreate 中添加代码:int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){ …ShowControlBar( &m_wndTestBar, FALSE, FALSE ); // 关闭测试 工具栏

return 0;}

Page 40: 第 6 章框架窗口界面设计

6.3 工具栏(9)    在 CMainFrame::OnViewTest 和 CMainFrame::OnTestReturn 中添加代码:

void CMainFrame::OnViewTest() { …ShowControlBar( &m_wndTestBar, TRUE, FALSE );// 显示测试工具栏ShowControlBar( &m_wndToolBar, FALSE, FALSE );// 关闭主工具栏 }void CMainFrame::OnTestReturn() { …ShowControlBar( &m_wndTestBar, FALSE, FALSE );// 关闭测试工具栏ShowControlBar( &m_wndToolBar, TRUE, FALSE );// 显示主工具栏 }

(10) 编译运行并测试,如图,左边开始运行的结果,右边是单击按钮运行的结果。

Page 41: 第 6 章框架窗口界面设计

6.4 状态栏 6.4.1状态栏的定义 AppWizard 创建的 SDI或 MDI 应用程序框架中,有一个静态的 indicator数组,是在 MainFrm.cpp 文件中定义的,被 MFC 用作状态栏的定义。 数组中的元素是一些标识常量或是字串资源的 ID号。图列出了 indicators数组元素与标准状态栏窗格的关系。

Static UINT indicators[]={ID_SEPARATOR,ID_INDICATOR_CAPS,ID_INDICATOR_NUM,ID_INDICATOR_SCRL,}

图 6.28 indicators 数组的定义

Page 42: 第 6 章框架窗口界面设计

6.4 状态栏6.4.2 状态栏的常用操作 增加和减少窗格 在状态栏中增加信息行窗格,在 indicators 数组中增加一个 ID_SEPARATOR标识;在状态栏中增加指示器窗格,在 indicators 数组中增加一个在字符串表中定义过的资源 ID 。状态栏减少一个窗格,只需减少 indicators 数组元素。 在状态栏上显示文本 调用 CStatusBar::SetPaneText函数更新任何窗格中的文本。函数原型:BOOL SetPaneText( int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE ); 示例 (1)  创建一个默认的单文档应用程序 Ex_SDIMouse 。(2)  将工作区切换到 ClassView页面,展开 CMainFrame ,双击 CMainFrame ,在文档窗口中出现该函数的定义,在它的前面就是状态栏数组的定义。(3)  将状态栏 indicators 数组的定义改为下列代码:static UINT indicators[] ={ ID_SEPARATOR, ID_SEPARATOR, };

Page 43: 第 6 章框架窗口界面设计

6.4 状态栏(4) 在 CEx_SDIMouseView 类中添加 CMainFrame 类的代码。 CEx_SDIMouseView::OnMouseMove 代码:

void CEx_SDIMouseView::OnMouseMove(UINT nFlags, CPoint point) {CString str;CMainFrame* pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd; // 获得主窗口指针CStatusBar* pStatus=&pFrame->m_wndStatusBar;

// 获得主窗口中的状态栏指针if (pStatus) {

str.Format("X=%d, Y=%d",point.x, point.y);// 格式化文本pStatus->SetPaneText(1,str);// 更新第二个窗格的文本

}CView::OnMouseMove(nFlags, point);

}

Page 44: 第 6 章框架窗口界面设计

6.4 状态栏(5)  将 MainFrm.h 文件中的受保护变量m_wndStatusBar变成公共变量。(6)  在 Ex_SDIMouseView.cpp 文件的开始处增加下列语句:#include "Ex_SDIMouseView.h"#include "MainFrm.h“

(7) 编译并运行,如图。

Page 45: 第 6 章框架窗口界面设计

6.4 状态栏6.4.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表示窗格是凸起来的,见表。

Page 46: 第 6 章框架窗口界面设计

6.5 交互对象的动态更新 在前面 Ex_SDI 基础上,单击工具按钮,该按钮被按下,按钮弹起,单击工具按钮时,该按钮被按下,按钮弹起,如此反复。具体步骤: (1)    打开上例的单文档应用程序项目 Ex_SDI 。(2)   为 CMainFrame添加一个 BOOL型的成员变量m_bMainWork,将 m_bMainWork的初值设为 TRUE 。(3)  在 CMainFrame 类中添加工具按钮 ID_VIEW_TEST和 ID_TEST_RETURN的 UPDATE_COMMAND_UI消息映射函数,添加代码:void CMainFrame::OnUpdateViewTest(CCmdUI* pCmdUI) { pCmdUI->Enable( TRUE );pCmdUI->SetCheck( !m_bMainWork ); }void CMainFrame::OnUpdateTestReturn(CCmdUI* pCmdUI) { pCmdUI->Enable( TRUE );pCmdUI->SetCheck( m_bMainWork ); }

Page 47: 第 6 章框架窗口界面设计

6.5 交互对象的动态更新(4) 在 CMainFrame::OnViewTest 和 CMainFrame::OnTestReturn添加代码:

void CMainFrame::OnViewTest() { …

m_bMainWork = FALSE;}void CMainFrame::OnTestReturn() { …

m_bMainWork = TRUE;}

(5) 编译运行并测试,如图。

Page 48: 第 6 章框架窗口界面设计

6.6图标和光标 6.6.1 使用图形编辑器 创建一个新的图标或光标

创建应用程序后,按快捷键 Ctrl+R打开“插入资源”,选择 Cursor(光标 )或 Icon( 图标 ) 资源类型,单击 [新建 ]。如图。创建新图标或光标的时,图形编辑器先创建的是一个适合于 VGA 环境中的图像,开始的时候它以屏幕色 (透明方式 ) 来填充。如表。

新设备按钮

Page 49: 第 6 章框架窗口界面设计

6.6图标和光标 选用和定制显示设备 图形编辑器上,有一个 New Device Image按钮,单击,如图。 单击 [定制 ],如图。 设置光标热点 借助光标“热点”确定光标实际的位置。如图。

设置热点按钮

Page 50: 第 6 章框架窗口界面设计

6.6图标和光标6.6.2 图标 图标的调入和清除 CWinApp::LoadIcon 可将一个图标资源调入并返回一个图标句柄。原型:HICON LoadIcon( LPCTSTR lpszResourceName ) const;HICON LoadIcon( UINT nIDResource ) const; 不想创建新的图标资源,可使用预定义的标准图标,调用 CWinApp::LoadStandardIcon ,原型:HICON LoadStandardIcon( LPCTSTR lpszIconName ) const;lpszIconName 可以是下列值之一:IDI_APPLICATION 默认的应用程序图标IDI_HAND 手形图标 ( 用于严重警告 )IDI_QUESTION 问号图标 ( 用于提示消息 )IDI_EXCLAMATION 警告消息图标 (惊叹号 )IDI_ASTERISK 消息图标DestroyIcon 可以用来删除一个图标,释放为图标分配的内存,原型:BOOL DestroyIcon( HICON hIcon );hIcon 用来指定要删除的图标句柄。 图标的绘制 函数 CDC::DrawIcon 将一个图标绘制在指定的位置处,原型:BOOL DrawIcon( int x, int y, HICON hIcon );BOOL DrawIcon( POINT point, HICON hIcon );

Page 51: 第 6 章框架窗口界面设计

6.6图标和光标 应用程序图标的改变

使用 GetClassLong 和 SetClassLong重新指定应用程序窗口的图标,原型:DWORD SetClassLong( HWND hWnd, int nIndex, LONG dwNewLong);DWORD GetClassLong( HWND hWnd, int nIndex);hWnd 用来指定窗口类句柄, dwNewLong 用来指定新的 32 位值。 nIndex 用来指定与 WNDCLASSEX结构相关的索引,它可以是下列值之一:GCL_HBRBACKGROUND 窗口类的背景画刷句柄GCL_HCURSOR 窗口类的的光标句柄GCL_HICON 窗口类的的图标句柄GCL_MENUNAME 窗口类的的菜单资源名称

示例 [ 例 Ex_Icon] 改变应用程序图标。(1)    用 MFC AppWizard 创建一个默认的单文档应用程序 Ex_Icon 。(2) 新添四个图标资源,通过图像设备选择对话框 ( 参见图 6.32) 。图标资源 ID号分别为默认的 IDI_ICON1~ IDI_ICON4 。

Page 52: 第 6 章框架窗口界面设计

6.6图标和光标(3)    用图形编辑器绘制图标,如图。(4)    CMainFrame 类添加成员函数 ChangeIcon 。函数代码:void CMainFrame::ChangeIcon(UINT nIconID){ HICON hIconNew = AfxGetApp()->LoadIcon(nIconID);HICON hIconOld = (HICON)GetClassLong(m_hWnd, GCL_HICON);if (hIconNew != hIconOld){ DestroyIcon(hIconOld);SetClassLong(m_hWnd, GCL_HICON, (long)hIconNew);RedrawWindow(); // 重绘窗口}}

Page 53: 第 6 章框架窗口界面设计

6.6图标和光标(5)    在 CMainFrame::OnCreate函数的最后添加计时器设置代码:int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){ if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;...SetTimer(1, 500, NULL);return 0;} (6) 为 CMainFrame 类添加 WM_TIMER 的消息映射函数,增加代码: void CMainFrame::OnTimer(UINT nIDEvent) { static int icons[] = { IDI_ICON1, IDI_ICON2, IDI_ICON3, IDI_ICON4}; static int index = 0;ChangeIcon(icons[index]);index++;if (index>3) index = 0;CFrameWnd::OnTimer(nIDEvent);}

Page 54: 第 6 章框架窗口界面设计

6.6图标和光标(7)    为 CMainFrame 类添加 WM_DESTROY 的消息映射函数,增加代码:

void CMainFrame::OnDestroy() { CFrameWnd::OnDestroy();KillTimer(1);}KillTimer函数是 CWnd 类成员函数,用来停止 WM_TIMER消息的传送,其函数参数值用指定要停用的计时器标识值。

(8) 编译并运行。可以看到任务栏上的按钮以及应用程序的标题栏上四个图标循环显示的动态效果,显示速度为每秒两帧。

Page 55: 第 6 章框架窗口界面设计

6.6图标和光标6.6.3 光标 使用系统光标 标准光标可以使用 CWinApp:: LoadStandardCursor 加载到程序中,函数:HCURSOR LoadStandardCursor( LPCTSTR lpszCursorName ) const;lpszCursorName 用来指定一个标准光标名,可以是下列宏定义:IDC_ARROW 标准箭头光标IDC_IBEAM 标准文本输入光标IDC_WAIT 漏斗型计时等待光标IDC_CROSS 十字形光标IDC_UPARROW 垂直箭头光标IDC_SIZEALL 四向箭头光标IDC_SIZENWSE 左上至右下的双向箭头光标IDC_SIZENESW 左下至右上的双向箭头光标IDC_SIZEWE 左右双向箭头光标IDC_SIZENS 上下双向箭头光标 使用光标资源 用编辑器创建或从外部调入的光标资源,通过函数 CWinApp::LoadCursor 进行加载,原型:HCURSOR LoadCursor( LPCTSTR lpszResourceName ) const;HCURSOR LoadCursor( UINT nIDResource ) const;

Page 56: 第 6 章框架窗口界面设计

6.6图标和光标 更改程序中的光标 更改程序中的光标最简单的方法是 MFC ClassWizard 映射WM_SETCURSOR消息,该消息是光标移动到一个窗口内并且还没有捕捉到鼠标时产生的。 CWnd 为此消息的映射函数定义这样的原型:afx_msg BOOL OnSetCursor( CWnd* pWnd, UINT nHitTest, UINT message );pWnd表示拥有光标的窗口指针, nHitTest 用来表示光标所处的位置。 message用来表示鼠标消息。在 OnSetCursor函数调用 SetCursor 来设置相应的光标,并将 OnSetCursor函数返回TRUE ,就可改变当前的光标了。

示例 (1)  打开前面的单文档应用程序 Ex_Icon 。(2)  按 Ctrl+R ,打开“插入资源”对话框,选择“ Cursor”类型后,单击 [新建 ]。(3)  图形编辑器工作窗口的控制条上,单击“ New Device Image”,单击 [ 定制 ]。(4)   在弹出的“定制图像”对话框中,保留默认的大小和颜色数,单击 [确定 ]。。(5)   选择“ 32 x 32, 16 colors”设备类型,单击 [确定 ]按钮。(6) 在“ Device”组合框中,选择“ Monochrome [32 x 32 ]”,选择“ Delete

Device Image”命令,删除“ Monochrome [32 x 32 ]”类型。

Page 57: 第 6 章框架窗口界面设计

6.6图标和光标(7) 用图形编辑器绘制光标图形,指定光标热点位置为 (15, 15) ,如图。

Page 58: 第 6 章框架窗口界面设计

6.6图标和光标(8)    为 CMainFrame 类添加成员变量 m_hCursor ,变量类型为光标句柄

HCURSOR 。(9)    为 CMainFrame 类添加 WM_SETCURSOR 的消息映射函数,增加代码:BOOL CMainFrame::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { BOOL bRes = CFrameWnd::OnSetCursor(pWnd, nHitTest, message);if (nHitTest == HTCAPTION ) {

m_hCursor = LoadCursorFromFile("c:\\windows\\cursors\\globe.ani");SetCursor(m_hCursor);bRes = TRUE;

}else if (nHitTest == HTCLIENT ) { m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR1);

SetCursor(m_hCursor);bRes = TRUE;

}return bRes;}

(10)  编译运行并测试。当鼠标移动到标题栏时,光标变成了 globe.ani 的动画光标,而当移动到客户区时,光标变成了 IDC_CURSOR1 定义的形状。