1、3.1 定时器消息 3.2 键盘消息 3.3 鼠标消息 3.4 消息映射工作 3.5 菜单、工具条、状态条,第三章 与用户交互,3.1 定时器消息,在许多需要控制定时完成某些操作或周期行的工作时,需要使用系统提供的定时器。所有窗口类及其子类都可以创建、使用定时器。,设置定时器函数: SetTimer(UNIT nIDEvent,UNIT nElapse,NULL) ; 定时器定时发送WM_TIMER消息,其消息处理函数为: OnTimer(UINT nIDEvent) 如果设置了一个以上的定时器,可以通过参数的值识别。 终止定时器函数: KillTimer( int nIDEvent);,课堂
2、练习:1. 请用VC+6.0编写一单文档应用程序,实现自动打字的功能,即每隔一定时间往视图上输出一个字母。,实现方法:在视图类中添加变量:int m_col; int m_row; int m_count; BOOL m_bauto;,在视图类的构造函数中:m_bauto=FALSE;,在“结束”菜单的执行语句: KillTimer(1); m_bauto=FALSE;,在“开始”菜单的执行语句:m_bauto=TRUE;m_count=0;m_row=0;m_col=0;srand(unsigned)time(NULL);SetTimer(1,100,0);,WM_TIMER消息的消息处理函
3、数:int nrand,nbase;KillTimer(1);nbase=(int)a;nrand=rand()%26+nbase;CDC *pdc=GetDC();m_count+; CString str;str.Format(“%c“,nrand);pdc-TextOut(m_col*20,m_row*20,str);m_col+;if(m_count%10=0) m_row+; m_col=0;if (m_count100)SetTimer(1,100,0);elsem_bauto=FALSE;,3.2 键盘消息,1 扫描码、虚拟键码和ASCII码,对应键盘上每一个有意义的按键有一个唯
4、一的标识符,这个唯一的标识符被称为扫描码。它是一组8位代码。在按键被按下、释放的时候都会产生这个扫描码。它和具体的硬件设备是相关的。,虚键码是对应键盘上的键的一种具有设备无关性的标识符。,VK_ A VK_ B VK_ C VK_ F1VK_ F2 VK_ RETURN VK_ DELETE,消息的结构 typedef struct tagMSG HWND hwnd; UINT message; WPARAM wParam; LPARAM Iparam; DWORD time; POINT pt; MSG;hwnd:标识获得消息的窗口进程的窗口句柄。 message:指定消息值。 wParam
5、:消息的第一个参数。 Iparam:消息的第二个参数。 time:指定消息发送时的时间。 pt:以屏幕坐标表示消息发送时的鼠标指针的位置。,虚键码,在按键被按下 时候,就会产生一个键盘消息, 这个消息包含按键的扫描码、虚键码以及和按键动作有关的信息。设备驱动程序把这个消息放到系统消息队列中。Windows系统从系统消息队列中取出这类消息,在把它发送到相应的应用程序的消息队列中。最后,应用程序从自己的消息队列中取出这个消息,进行相应的处理。,2 键盘消息的产生过程,键盘消息通常只发送到当前的输入焦点窗口。 处理键盘消息的前提:需要窗口不被最小化。,3 系统键和非系统键凡是【Alt】键和其它键一同
6、按下的键组合称为系统键, 其余是非系统键。键盘消息分为系统键消息和非系统消息。,非系统键消息:当按下键盘时,Windows发送WM_KEYDOWN消息,然后发送WM_CHAR消息,当松开键盘时,发送WM_KEYUP消息。,系统键消息: WM_SYSKEYDOWN、WM_SYSKEYUP和WM_SYSCHAR。,afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);nChar不是虚拟键码,而是可打印字符 。,afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
7、afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);nChar参数指定了所按击键的虚键。nRepCut参数指定重复次数,用于检测一个键是否被按住不放且请求打字重复功能。nFlags参数用来指定附加信息,如一个键被按下的同时是否“A1t”键也被按下。,系统键通常由Windows系统内部处理,如果应用程序需要处理它,在相应的消息处理函数中,除了用户实现的功能以外,在函数的最后还要调用DelWindowsProc()函数,以不影响Windows系统对该消息的处理。,课堂练习: 1.键盘上下左右键的鼠标光标移动 2.当用户按下空格键时
8、,鼠标光标移至一个随机位置.,CPoint ptcurpos; if(:GetCursorPos( case VK_DOWN: 。 ,ptcurpos.x=:rand( )%:GetSystemMetrics(SM_CXSCREEN); ptcurpos.y=:rand( )%:GetSystemMetrics(SM_CYSCREEN);,3.3 鼠标消息,产生:当移动或按下鼠标时 接收:鼠标经过的窗口或鼠标点击过的窗口接收条件:鼠标的光标位于某一窗口的客户区或非客户区时,单击,双击,移动,拖动 ?,系统默认 500 豪秒 SetDoubleClickTime(),移动鼠标: WMMOUSEM
9、0VE按下鼠标键: WMxBUTTONDOWN放开鼠标键: WMxBUTTONUP双击鼠标键: WMxBUTTONDBCLK左键 x=L 右键 x=R 中键x=M,客户区消息,非客户区消息,客户区鼠标消息响应函数原型:afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMButtonDblC
10、lk(UINT nFlags, CPoint point); afx_msg void OnMButtonDown(UINT nFlags, CPoint point); afx_msg void OnMButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point); afx_msg void OnRButtonDown(UINT nFlags, CPoint poin
11、t); afx_msg void OnRButtonUp(UINT nFlags, CPoint point);,注意: 通常情况下,只有当鼠标的光标位于某一窗口的客户区时,才能接收到鼠标消息. 捕捉鼠标函数: SetCapture() 释放鼠标函数: ReleaseCapture(),nFlags参数包含标识按下鼠标和键盘状态的位组, 表明哪种虚拟键被按下。可以是下表所示各值的组合。point的意义是鼠标的光标坐标值。 这坐标值标明了鼠标光标的热点位置。,afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);,课堂练习:1. 请用VC
12、+6.0编写一单文档应用程序,实现在运行时任何时刻单击鼠标左按钮,就在鼠标光标位置处显示文字。,void CKeyView:DrawBoo(CDC* pdc,CPoint ptPos) static char sz=“hello“; pdc-TextOut(ptPos.x,ptPos.y,sz,sizeof(sz)-1);,void CKeyView:OnLButtonDown(UINT nFlags, CPoint point) CClientDC dc(this);DrawBoo( ,void CKeyView:DrawBoo(CDC* pdc,CPoint ptPos) CDC dcme
13、m;CBitmap bmboo;CBitmap * pbmold;BITMAP bminfo;CSize sizboo; VERIFY(bmboo.LoadBitmap(IDB_BOO); bmboo.GetObject(sizeof(BITMAP),显示位图:,3.4 消息映射的工作,什么是消息映射?,消息映射是MFC的一种结构,通常用来改进应用程序处理消息的方式。消息映射将消息与消息处理函数(用于处理发送来的消息的成员函数)联系起来。,2.消息映射宏的用法:,(1)响应消息的类必须用一套消息映射宏表明这个类将处理消息.在该类头文件中类声明的结束处,必须加上以下宏语句: DECLARE_ME
14、SSAGE_MAP(),所有能够接收消息的类,都应该派生自CCmdTarget,# define DECLARE_MESSAGE_MAP() Private: Static const AFX_MSGMAP_ENTRY_messageEntries; Protected: Static AFX_DATA const AFX_MSGMAP messageMap; Virtual const AFX_MSGMAP* GetMessageMap() const;,DECLARE_MESSAGE_MAP宏相当于声明了这样的数据结构,(2) 类的实现文件必须加上以下两个宏语句: BEGIN_MESSAG
15、E_MAP( theClass,baseClass) / Message map entries go here END_MESSAGE_MAP(),例:BEGIN_MESSAGE_MAP( TTApp,CWinApp) ON _ COMMAND(ID _FIEL _NEW,CWinApp:OnFileNew) ON _ COMMAND(ID _FIEL _OPEN,CWinApp:OnFileOpen) ON _ COMMAND(ID _APP _ABOUT,OnAppAbout) END_MESSAGE_MAP(),MFC把消息主要分为三大类: 1.标准Windows消息(WM_xxx),
16、MFC消息映射表(消息传递网),如果一个类没有提供处理某个特殊消息的消息处理函数。则消息通过树传到父类。进程就持续下去直至消息影射的所有基类都被搜索一遍。如果没有一个类处理该消息,它就由缺省进程处理。,2.命令消息(WM_COMMAND) 宏命令:ON _COMMAND(,),依据菜单的不同作用进行不同映射。 如果该菜单用于文档的显示编辑,则最好在视图类中映射。 如果该菜单用于文档的打开和存储,则最好在文档类中映射。 对于通用菜单,可在框架类中映射。 About对话框的显示菜单常在应用类中映射。,对于MDI应用程序命令传递顺序为: 视图-文档-MDI子窗口-MDI主框架窗口-应用程序 对于SD
17、I应用程序命令传递顺序为: 视图-文档-SDI主框架窗口-应用程序,3.Notification 消息(BN_xxx),作业: 1.请用VC+6.0编写一单文档应用程序,实现一个可编辑文字,并且能够绘画的功能。,void CPictureView:OnLButtonDown(UINT nFlags, CPoint point) st=point;CEditView:OnLButtonDown(nFlags, point); void CPictureView:OnLButtonUp(UINT nFlags, CPoint point) st.x=-1;CEditView:OnLButtonUp
18、(nFlags, point); void CPictureView:OnMouseMove(UINT nFlags, CPoint point) CRect rcclient;GetClientRect( ,(1)定义消息ID #define WM_FINISH WM_USER+10 (在消息响应的类的头文件中),4.自定义消息,(2)定义消息响应函数afx_msg void OnFinish(); (在宏DECLAREMESSAGEMAP()之前),(3)定义ONMESSAGE宏 ON_MESSAGE(WM_FINISH,CPictureView:OnFinish)(在宏BEGINMESS
19、AGEMAP()和 ENDMESSAGEMAP()之间),(5)发送消息 SendMessage (WM_FINISH); (在合适的函数中) void CPictureView:OnLButtonUp(UINT nFlags, CPoint point),(4)实现消息响应函数 void CPictureView:OnFinish() MessageBox(“绘画结束!”);,( 3 )编写消息处理函数,编写消息处理函数与编写一般的成员函数有些不同。 在函数声明处有afx-msg关键字。标识该函数为消息处 理函数。在方法的具体实现中,不在需要该关键字。,处理消息必需的几个步骤: 1.在适当的
20、文件中声明消息处理函数 2.创建消息映射,把消息同消息处理函数绑定在一起. 3.在适当的源代码文件中写入消息处理代码,课堂练习: 1.将上述程序添加配置菜单,实现画面清除和字体设置功能,并相应填加工具条,在状态栏中能够显示鼠标移动时 X 和 Y 的坐标值。,void CPictureView:OnClear() CRect ClientRect;GetClientRect( ,void CPictureView:OnUpdateClear(CCmdUI* pCmdUI) pCmdUI-Enable(m_finish=FALSE); ,Enable() SetCheck() SetRadio()
21、 SetText(),1. 创建菜单CMenu类的CreateMenu和CreatePopupMenu分别用来创建一个菜单或子菜单框架,它们的原型如下:BOOL CreateMenu( ); / 产生一个空菜单BOOL CreatePopupMenu( ); / 产生一个空的弹出式子菜单2. 装入菜单将菜单从资源装入应用程序中,需调用CMenu成员函数LoadMenu,或者用SetMenu对应用程序菜单进行重新设置。BOOL LoadMenu( LPCTSTR lpszResourceName );BOOL LoadMenu( UINT nIDResource );其中,lpszResourc
22、eName为菜单资源名称,nIDResource为菜单资源ID标识符。,3.5 菜单、工具条、状态条,3. 添加菜单项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
23、 );BOOL InsertMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, const CBitmap* pBmp );其中,nIDNewItem表示新菜单项的资源ID标识符,lpszNewItem表示新菜单项的内容,pBmp用于菜单项的位图指针,nPosition表示新菜单项要插入的菜单项位置。nFlags表示要增加的新菜单项的状态信息,它的值影响其他参数的含义,如下表所示。,需要注意的是: 当nFlags为MF_BYPOSITION时,nPosition表示新菜单项要插入的具体位置,为0时表示第一个菜单项,为 -1时,将菜单项添加菜单
24、的末尾处。 nFlags的标志中,可以用“|”(按位或)来组合,例如MF_CHECKED|MF_STRING等。但有些组合是不允许的,例如MF_DISABLED、MF_ENABLED和MF_GRAYED,MF_STRING、MF_OWNERDRAW、MF_SEPARATOR和位图,MF_CHECKED和MF_UNCHECKED 都不能组合在一起。 当菜单项增加、改变或删除后,不管菜单依附的窗口是否改变,都应调用CWnd: DrawMenuBar来更新菜单。4. 删除菜单项BOOL DeleteMenu( UINT nPosition, UINT nFlags );其中,参数nPosition表
25、示要删除的菜单项位置,它由nFlags进行说明。若当nFlags为MF_BYCOMMAND时,nPosition表示菜单项的ID标识符,而当nFlags为MF_BYPOSITION时,nPosition表示菜单项的位置(第一个菜单项位置为0)。,获取菜单项:下面的3个CMenu成员函数分别获得菜单的项数、菜单项的ID标识符以及弹出式子菜单的句柄。UINT GetMenuItemCount( ) const;该函数用来获得菜单的菜单项数,调用失败后返回-1。UINT GetMenuItemID( int nPos ) const;该函数用来获得由nPos指定菜单项位置(以0为基数)的菜单项的标识
26、号,若nPos是SEPARATOR(分隔符),则返回-1。CMenu* GetSubMenu( int nPos ) const;该函数用来获得指定菜单的弹出式菜单的菜单句柄。该弹出式菜单位置由参数nPos指定,开始的位置为0。若菜单不存在,则创建一个临时的菜单指针。,课堂练习:,int CMainFrame:OnCreate(LPCREATESTRUCT lpCreateStruct) . CMenu* pSysMenu = GetMenu(); / 获得程序菜单指针 CMenu* pSubMenu = pSysMenu-GetSubMenu(1);/ 获得第二个子菜单的指针 CString
27、 strMenuItem(“新的菜单项“); pSubMenu-AppendMenu(MF_SEPARATOR); / 增加一水平分隔线 pSubMenu-AppendMenu(MF_STRING,ID_NEW_MENUITEM, strMenuItem); / 在子菜单中增加一菜单项m_bAutoMenuEnable = FALSE; / 关闭自动更新菜单状态,这样避免添加的菜单项是禁用的 pSysMenu- EnableMenuItem(ID_NEW_MENUITEM,MF_BYCOMMAND|MF_ENABLED);/ 激活菜单项 DrawMenuBar(); / 更新菜单return
28、0;,BOOL CMainFrame:OnCommand(WPARAM wParam, LPARAM lParam) / wParam的低字节表示菜单、控件、加速键的命令IDif (LOWORD(wParam) = ID_NEW_MENUITEM) MessageBox(“你选中了新的菜单项“);return CFrameWnd:OnCommand(wParam, lParam);,使用快捷菜单BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );该函数用来显示一个浮动的弹出式菜单
29、,其位置由各参数决定。其中,nFlags表示菜单在屏幕显示的位置以及鼠标按钮标志,如下表所示。x和y表示菜单的水平坐标和菜单的顶端的垂直坐标。pWnd表示弹出菜单的窗口,此窗口将收到菜单全部的 WM_COMMAND消息。lpRect是一个RECT结构或CRect类指针,它表示一个矩形区域, 用户单击这个区域时,弹出菜单不消失。而当lpRect为NULL时,若用户击在菜单外面, 菜单立刻消失。,nFlags的值及其对其他参数的影响,产生浮动菜单: 方法(一)添加对单击鼠标右键的处理函数,void CCdView:OnRButtonUp(UINT nFlags, CPoint point) CMe
30、nu pmenu; pmenu.LoadMenu(IDR_MENU1); ClientToScreen( ,产生浮动菜单: 方法(二)添加对信息WM_CONTEXTMENU的处理函数,void CPrintView:OnContextMenu(CWnd* pWnd, CPoint point) CMenu menu;menu.LoadMenu(IDR_MENU1);menu.GetSubMenu(0)-TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,this); ,思考:如何使浮动菜单自动更新? 提示:EnableMen
31、uItem(UNIT nIDEnableItem,UNIT nEnable),MF_GRAYED MF_ENABLED,状态栏SDI或MDI应用程序框架中,有一个静态的indicator数组,它是在MainFrm.cpp文件中指定的,被MFC用作状态栏的定义。,Static UINT indicators= ID_SEPARATOR,ID_INDICATOR_CAPS,ID_INDICATOR_NUM,ID_INDICATOR_SCRL, ,状态栏的常用操作Visual C+ 6.0中可以方便地对状态栏进行操作,如增减窗格、在状态栏中显示文本、改变状态栏的风格和大小等,并且MFC的CStatu
32、sBar类封装了状态栏的大部分操作。1. 增加和减少窗格状态栏中的窗格可以分为信息行窗格和指示器窗格两类。若在状态栏中增加一个信息行窗格,则只需在indicators数组中的适当位置中增加一个ID_SEPARATOR标识即可;若在状态栏中增加一个用户指示器窗格,则在indicators数组中的适当位置增加一个在字符串表中定义过的资源ID,其字符串的长度表示用户指示器窗格的大小。若状态栏减少一个窗格,其操作与增加相类似,只需减少indicators数组元素即可。,2. 在状态栏上显示文本调用CStatusBar:SetPaneText函数可以更新任何窗格(包括信息行窗格)中的文本。BOOL Se
33、tPaneText( int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE );其中,lpszNewText表示要显示的字符串。nIndex是表示设置的窗格索引(第一个窗格的索引为0)。若bUpdate为TRUE,则系统自动更新显示的结果。,CString str; CMainFrame* pFrame=(CMainFrame*)AfxGetApp()-m_pMainWnd; / 获得主窗口指针 CStatusBar* pStatus= ,在 OnMouseMove()函数中添加:,AfxGetApp是CWinApp类的一个成员函数,该函数可在应用程序项目中的任何类中使用,用来获取应用程序中的CWinApp类对象指针。将MainFrm.h文件中的受保护变量m_wndStatusBar变成公共变量。在pictureView.cpp文件的开始处增加下列语句: #include “MainFrm.h“,CCmdUI 类的成员函数对用户交互对象的作用,