第二篇
VC常用类、控件、资源篇第4章基本输入—键盘和鼠标消息
Windows采用的是事件驱动机制,因此要让程序完成一定功能,很多情况下用户必须进行干预,
告诉程序要干什么,这就要求用户通过某种手段与计算机进行交互。鼠标和键盘是用户与Windows应用程序交换的最主要的交换设备。Windows预先定义了大量的鼠标消息和键盘消息,而MFC也对其中的常用消息进行了封装。本章主要介绍在MFC应用程序中,键盘和鼠标消息的响应与处理。
4.1 键盘消息及其处理在Windows中,键盘输入以消息的形式传递给程序的视窗消息处理程序。按下或释放一个键均会产生一条消息,系统将其放到与键盘输入窗口相对应的线程消息队列中。
4.1.1 键盘消息键盘消息有三种:键盘被按下、键盘被松开(弹起)、输入字符。其中,输入字符相当于直接得到用户输入的字符,这在不需要处理按键细节时使用;而键盘被按下、松开在按键状态改变时发送。
MFC对键盘的这三种键盘消息进行了封装,如表4.1所示。
按键和释放键消息通常是成对出现的,但如果用户按住键到一定的时间就启动了键盘的自动重复特性,系统就会产生一系列的WM_KEYDOWN消息,
在用户释放一个键时,才产生一条WM_KEYUP消息。
4.1.2 键盘消息的处理一般来讲,用户输入消息,如菜单选择、鼠标、键盘等,多在文档/视图结构的应用程序中使用。
在MFC应用程序开发中,可以使用ClassWizard为键盘消息和鼠标消息添加消息映射和消息处理函数。
在MFC文档/视图结构的应用程序中,视图窗口和框架窗口均能处理键盘和鼠标消息。当有键盘或鼠标消息产生时,系统会先将消息发往视图窗口,如果视图类中没有发现相应的消息映射和处理函数,就将该消息再发往框架窗口。所以在使用ClassWizard为键盘消息定义消息映射时,定义在视图
71
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息表4.1 MFC中封装的键盘消息键盘消息描述
WM_KEYDOWN键盘按下
WM_KEYUP键盘抬起
WM_CHAR可打印字符键按下并抬起,输入了一个字符类中就可以了。如果一个应用同时拥有多个视图,而当前活动没有对消息进行处理,则消息会发往框架窗口。
下面通过具体的实例讲解键盘消息的映射及处理。
4.1.3 按键消息的处理当按下一个键或松开一个键时,将产生一个按键消息。在MFC中,WM_KEYDOWN和
WM_KEYUP消息分别在按下一个键和松开一个键时被激发。
本节将创建一个单文档应用程序,当用户按下任意一个键时,弹出对话框显示相应的提示信息。
具体实现过程如下。
1,使用MFC AppWizard创建SDI工程启动Visual C++6.0,执行“File”→“New”菜单命令,在“Projects”选项卡中,创建一个MFC
AppWizard[exe]工程,工程名为“Ch5Demo1”,如图4.1所示。
单击“OK”按钮,在“MFC AppWizard Step 1”对话框中,选中“Single document”,即基于单文档的MFC工程,其余的几步向导对话框均采用默认设置。
2,添加WM_KEYDOWN消息映射和响应函数执行“View”→“ClassWizard”菜单命令,或者使用【Ctrl+W】快捷键,打开“MFC
ClassWizard”对话框。在“Message Maps”选项卡的“Class name”列表框中选择要响应键盘消息的类“CCh5Demo1View”,在“Object IDs”列表框中选择“CCh5Demo1View”,在“Messages”列表框中选择“WM_KEYDOWN”按键消息。
单击“Add Function”按钮,ClassWizard自动为WM_KEYDOWN添加了ON_ WM_KEYDOWN
消息映射宏和OnKeyDown()消息处理函数,如图4.2所示。
此时,在“Ch5Demo1View.cpp”资源文件的开始部分会发现自动添加的消息映射代码:
BEGIN_MESSAGE_MAP(CCh5Demo1View,CView)
//{{AFX_MSG_MAP(CCh5Demo1View)
ON_WM_KEYDOWN() //消息映射代码
//}}AFX_MSG_MAP
72
励志照亮人生 编程改变命运零基础学 Visual C++
图4.1,New”对话框图4.2,MFC ClassWizard”对话框
// Standard printing commands
END_MESSAGE_MAP()
在“Ch5Demo1View.h”头文件,自动添加了消息处理函数OnKeyDown()的声明代码如下:
protected:
//{{AFX_MSG(CCh5Demo1View)
afx_msg void OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags);
//}}AFX_MSG
3,手工添加函数的实现代码在ClassWizard对话框(图4.2)中,单击“Edit Code”按钮,在工作区中,即跳转至新增函数代码的定义处。这时,需要手工添加OnDrawcircle()函数的实现代码,如下:
void CCh5Demo1View::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
AfxMessageBox("用户按下了键!"); //弹出提示对话框
CView::OnKeyDown(nChar,nRepCnt,nFlags);
}
这里只添加了一行代码,实现弹出提示对话框。
编译运行程序后,若用户按下键盘的任意键,就会弹出提示对话框,如图4.3所示。
WM_KEYUP消息的消息映射和处理过程与
WM_KEYDOWN的完全相同。
4.1.4 按键消息处理函数从上一节实例可见,MFC会将WM_KEYDOWN消息映射为ON_WM_KEYDOWN,而对应的处理函数为
OnKeyDown()。同样,WM_KEYUP消息对应的处理函数为OnKeyUp()。它们具有完全相同的参数,声明如下:
void OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags);
void OnKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags);
参数nChar表示按键的虚拟键码。如“VK_ALT”,表示按下的键为【Alt】键,
“VK_CONTROL”表示按下的键为【Ctrl】键。Windows中常用的虚拟键码及其对应的按键如表4.2所示。
表4.2 Windows常用的虚拟键码及其对应的按键虚拟键码数值对应的键虚拟键码数值对应的键
VK_LBUTTON 1鼠标左键VK_BACK 8退格键
VK_RBUTTON 2鼠标右键VK_TAB 9制表键
VK_MBUTTON 4鼠标中键VK_RETURN 13回车键
VK_SHIFT 16 Shift键VK_CONTROL 17 Ctrl键
73
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息图4.3 程序运行结果
(续)
虚拟键码数值对应的键虚拟键码数值对应的键
VK_MENU 18 Alt键VK_PAUSE 19 Pause键
VK_CAPTIAL 20 Caplock键VK_ESCAPE 27 Esc键
VK_PRIOR 33 Page Up键VK_NEXT 34 Page Down键
VK_END 35 End键VK_HOME 36 Home键
VK_LEFT 37左箭头键VK_RIGHT 39右箭头键
VK_UP 38上箭头键VK_DOWN 40下箭头键
48?57 0?9键65?90 A?Z键
VK_SPACE 32空格键VK_SNAPSHOT 44 Print Screen键
VK_INSERT 45 Insert键VK_DELETE 46 Delete键
112?135功能键F1?F24 VK_NUMLOCK 144 NumLock键使用虚拟键码时,可以使用键码也可以直接使用其数值。如判断按键是否为回车键时,可以采用
“if(nChar == VK_RETURN)”语句,也可采用“if(nChar == 13)”语句。
说明虚拟键码是Windows内部建立的设备无关的键盘代码。在Windows中不论使用什么类型的键盘,
都将扫描代码翻译成同一的虚拟键码,这样应用程序就不用直接同硬盘硬件打交道。
参数nRepCnt表示按键的重复次数,即长时间按住一个键时,相当于按键的次数。
参数nFlags中的不同位代表不同的含义,包括键盘扫描码、扩展键盘标志、描述码、键的先前状态和转换状态标志等,一般很少用到。
根据按键消息响应函数各参数的值,可以判断按键的相关信息,便于进行灵活的程序开发。下面给出一个具体应用实例。
实例功能:当用户同时按住【Ctrl】键和【F8】键时,激发某一操作,即弹出提示对话框。实现过程如下。
1,使用MFC AppWizard创建SDI工程按照4.1.3节介绍的“Ch5Demo1”工程的创建过程,创建MFC SDI工程“Ch5Demo2”。
2,添加WM_KEYDOWN和WM_UP消息映射和响应函数同样,利用ClassWizard,分别为视图类CCh5Demo2View添加WM_KEYDOWN、WM_UP消息映射和消息处理函数。
3,手工添加实现代码
(1)在视图类CCh5Demo2View的头文件“Ch5Demo2View.h”中,定义两个布尔变量,分别用于记录【Ctrl】键和【F8】键的状态:按下时为“true”,释放后为“false”。代码如下:
public:
BOOL b_Ctrldown,b_F8down;
(2)在CCh5Demo2View类的构造函数中,初始化变量为“false”。代码如下:
CCh5Demo2View::CCh5Demo2View()
74
励志照亮人生 编程改变命运零基础学 Visual C++
{
// TODO,add construction code here
b_Ctrldown=false; //初始化
b_F8down=false; //初始化
}
(3)在WM_KEYDOWN消息响应函数onkeyDown()中,实现按键状态的赋值,并在满足条件的情况下,弹出提示对话框。代码如下:
void CCh5Demo2View::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
if(nChar==VK_CONTROL) //如果按下了【Ctrl】键
{
b_Ctrldown=true;
}
if(nChar==VK_F8) //如果按下了【F8】键
{
b_F8down=true;
}
if(b_Ctrldown&&b_F8down) //同时按下了【Ctrl】键和【F8】键
{
b_Ctrldown=false;
b_F8down=false;
AfxMessageBox("同时按下了Ctrl键和F8键"); //弹出提示框
}
CView::OnKeyDown(nChar,nRepCnt,nFlags);
}
(4)在WM_KEYUP消息响应函数Onkeyup()中,实现按键状态的赋值。代码如下:
void CCh5Demo2View::OnKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
if(nChar==VK_CONTROL) //如果释放了【Ctrl】键
{
b_Ctrldown=false;
}
if(nChar==VK_F8) //如果释放了【F8】键
{
b_F8down=false;
}
CView::OnKeyUp(nChar,nRepCnt,nFlags);
}
编译运行程序后,若用户同时按下了【Ctrl】键和【F8】键,就会弹出提示对话框,如图4.4
所示。
75
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息图4.4 程序运行结果
4.1.5 字符消息的处理当按下一个可显示的字符(数字、字母、标点等)的键时,除了产生按下键消息
WM_KEYDOWN外,还将产生一个字符消息WM_CHAR。当然,如果释放该键,还将产生
WM_KEYUP消息。用户敲击一个一个字符键后,其产生的消息及处理函数的执行顺序如图4.5所示。
图4.5 键盘消息的处理过程
MFC将WM_CHAR消息映射为ON_WM_CHAR,其对应的处理函数为OnChar()。函数OnChar()
的定义如下:
void OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)
其中参数nRepCnt和nFlags的含义与4.1.3节介绍的OnKeyDown函数的完全相同。而参数nChar存放的不再是按键的虚拟键码,而是Windows字符集的字符代码,默认为ASCII码。键盘上常见的字符及其ASCII码值如表4.3所示。
表4.3 常见的字符及其ASCII码值字符符号ASCII码值字符符号ASCII码值字符符号ASCII码值
!33 "34 # 35
$36 % 7 & 8
'39 (40 ) 41
*42 + 3,
-45,46 / 47
76
励志照亮人生 编程改变命运零基础学 Visual C++
E
用户敲击字符按键处理函数键盘消息
(续)
字符符号ASCII码值字符符号ASCII码值字符符号ASCII码值
0~9 48~57,58 ; 59
<60 =61 > 62
63 @ 64 A~Z 65~90
[91 \92 ] 93
^94 _ 5 ` 6
a~z 97~122 { 123 | 124
} 125 ~ 126
如果nChar取值为64,则表示按键字符为“@”。下面给出一个在视图窗口中,显示键盘输入字符的实例。
该实例实现的功能是在单文档应用程序界面中,当用户通过键盘键入字符时,在视图窗口依次显示键入的字符。当用户按下【Enter】键时,进行换行输出。具体实现过程如下。
1,使用MFC AppWizard创建SDI工程按照4.1.3节介绍的“Ch5Demo1”工程的创建过程,创建MFC SDI工程“Ch5Demo3”。
2,添加WM_CHAR消息映射和响应函数执行“View”→“ClassWizard”菜单命令,或者使用【Ctrl+W】快捷键,打开“MFC
ClassWizard”对话框。在“Message Maps”选项卡的“Class name”列表框中选择要响应键盘消息的类“CCh5Demo3View”,在“Object IDs”列表框中选择“CCh5Demo3View”,在“Messages”列表框中选择“WM_CHAR”字符按键消息。
单击“Add Function”按钮,ClassWizard自动为WM_CHAR添加了ON_ WM_CHAR消息映射宏和OnChar()消息处理函数,如图4.6所示。
图4.6,MFC ClassWizard”对话框
77
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息
3,手工添加实现代码
(1)在视图类CCh5Demo3View的头文件“Ch5Demo3View.h”中,定义CPoint型变量,用于记录字符在视图窗口中的输出位置。代码如下:
public:
CPoint ptCharacter; //记录字符位置
(2)在CCh5Demo3View类的构造函数中,初始化ptCharacter位置为(0,0)。代码如下:
CCh5Demo3View::CCh5Demo3View()
{
// TODO,add construction code here
//初始位置设置在(0,0)
ptCharacter.x=0;
ptCharacter.y=0;
}
(3)在WM_CHAR消息响应函数OnChar()中,实现字符的显示以及换行。代码如下:
void CCh5Demo3View::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
if(nChar==13) //按下了回车键
{
//换行
ptCharacter.x=0;
ptCharacter.y=ptCharacter.y+25;
}
else
{
CClientDC dc(this);
dc.TextOut(ptCharacter.x,ptCharacter.y,(LPCTSTR)&nChar); //输出显示字符
CSize textsize;
textsize=dc.GetTextExtent((LPCTSTR)&nChar); //获取当前字符大小
//前进到下一个字符位置
ptCharacter.x=ptCharacter.x+textsize.cx;
}
CView::OnChar(nChar,nRepCnt,nFlags);
}
编译运行程序后,当用户通过键盘键入字符时,即会在视图窗口输出显示。如果按下了【Ente】键,将换行输出,如图4.7所示。
说明本程序只是简单演示了WM_CHAR消息响应和按键字符的显示操作,并没有实现窗口的重绘。另外,实际程序如果涉及文本输入、编辑操作,则是通过Edit控件和CEditView视图来实现的。
78
励志照亮人生 编程改变命运零基础学 Visual C++
图4.7 程序运行结果
4.1.6 创建键盘插入符键盘插入符(Caret)是一个闪烁的位图(通常是一个细的垂直杠),它可使用户知道在窗口何处可进行有效的键盘输入。
CWnd类提供了8个创建和管理键盘插入符的成员函数,如表4.4所示。
Windows总是把键盘消息送到拥有输入焦点的窗口。一般情况下,一个应用程序有多个窗口,而键盘消息只能被一个窗口接收,接收键盘消息的窗口称为有“输入焦点”的窗口,具有输入焦点的窗口称为活动窗口。当某一个窗口成为活动窗口时,
Windows会加亮显示其标题栏或窗口边框。
Windows用WM_SETFOCUS和WM_
KILLFOCUS消息通知即将通知接收或失去输入焦点的窗口。MFC分别对这两个消息进行了封装。
在窗口获得键盘焦点时,就可以创建插入符了,若窗口没有焦点,就不能进行键盘输入。另外,
插入符一旦创建起来,还要在窗口中对其进行定位和显示。下面通过具体的实例讲解插入符的使用。
下面在4.1.5节创建的“CCh5Demo3”实例的基础上,在字符将要显示的位置显示插入符。其具体实现过程如下。
1,打开工程启动Visual C++ 6.0,打开4.1.5节创建的“Ch5Demo3”工程文件“Ch5Demo3.dsw”。
2,添加WM_SETFOCUS消息映射和响应函数执行“View”→“ClassWizard”菜单命令,
或者使用【Ctrl+W】快捷键,打开“MFC
ClassWizard”对话框。在“Message Maps”选项卡中的“Class name”列表框中选择要响应捕获窗口消息的类“CCh5Demo3View”,在
“Object IDs”列表框中选择“CCh5Demo3View”,
在“Messages”列表框中选择“WM_
SETFOCUS”捕获窗口消息。
单击“Add Function”按钮,ClassWizard自动为WM_SETFOCUS添加了ON_
WM_SETFOCUS消息映射宏和OnSetFocus()消息处理函数,如图4.8所示。
3,手工添加实现代码
(1)在WM_SETFOCUS消息响应函数OnSetFocus()中,实现插入符的创建与显示。代码如下:
void CCh5Demo3View::OnSetFocus(CWnd* pOldWnd)
79
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息表4.4 创建和管理键盘插入符的CWnd成员函数函数功能
CreateCaret使用用户提供的位图创建插入符
CreateGrayCaret创建用户定义大小的实心灰色插入符
CreateSolidCaret创建用户定义大小的实心黑色插入符
DestoryCaret销毁插入符
ShowCaret显示插入符
HideCaret隐藏插入符
GetCaretPos返回插入符的位置
SetCaretPos移动插入符到窗口的某一位置图4.8,MFC ClassWizard”对话框
{
CView::OnSetFocus(pOldWnd);
// TODO,Add your message handler code here
CreateSolidCaret(4,20); //创建插入符
SetCaretPos (ptCharacter); //将插入符移到当前字符输入点
ShowCaret (); //显示插入符
}
(2)修改OnChar()函数,在适当的时机显示、隐藏插入符。代码如下:
void CCh5Demo3View::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
if(nChar==13) //按下了回车键
{
//换行
ptCharacter.x=0;
ptCharacter.y=ptCharacter.y+25;
SetCaretPos (ptCharacter); //将插入符移到键入点
ShowCaret (); //显示插入符
}
else
{
CClientDC dc(this);
HideCaret (); //隐藏插入符
dc.TextOut(ptCharacter.x,ptCharacter.y,(LPCTSTR)&nChar);//显示字符
CSize textsize;
textsize=dc.GetTextExtent((LPCTSTR)&nChar); //获取当前字符大小
//前进到下一个字符位置
ptCharacter.x=ptCharacter.x+textsize.cx;
SetCaretPos (ptCharacter); //将插入符移到键入点
ShowCaret (); //显示插入符
}
CView::OnChar(nChar,nRepCnt,nFlags);
}
编译运行程序后,会发现与原程序相比,在字符的输入位置增加了插入符,如图4.9所示。
图4.9 程序运行结果
80
励志照亮人生 编程改变命运零基础学 Visual C++
4.2 鼠标消息及其处理鼠标消息是应用程序开发中常需要处理的消息,
当鼠标移动、左键(右键)的按下或者松开、双击操作等都可以产生相应的鼠标消息。
4.2.1 鼠标消息在Windows中,鼠标消息分为两类:客户区鼠标消息和非客户区鼠标消息,其中包含了大量的鼠标消息。而在MFC中,只对其中常用的几种鼠标消息进行了封装,如表4.5所示。利用ClassWizard可以轻松地为这些消息添加消息映射和消息处理函数。
4.2.2 鼠标消息处理函数与键盘消息相同,当使用ClassWizard为鼠标消息添加消息映射时,系统自动为其添加了处理函数。
消息处理函数就是在消息名前去除“WM_”前缀,
换成“on”前缀,如对消息WM_LBUTTONDOWN,
消息映射宏的处理函数为OnLButtonDown(),其声明如下:
afx_msg void OnLButtonDown(UINT nFlags,CPoint point );
参数nFlags表明了当前一些按键的消息,其可取值如表4.6所示。
可以通过“位与”操作进行相关检测。在实际编程中,常使用nFlags参数指出消息生成时的鼠标键以及【Shift】键和【Ctrl】键的状态,如当按下鼠标左键时,同时检测【Shift】键和【Ctrl】键的状态,
可采用下面的代码:
void OnLButtonDown(UINT nFlags,CPoint point ) //按下了鼠标左键
{
if((nFlags &MK_CONTROL)&&( nFlags &MK_SHIFT)) //【Shift】和【Ctrl】键都被按下
...
}
参数point表示当前鼠标的设备坐标,坐标原点对应屏幕左上角。
通过point参数可以将鼠标操作与屏幕显示对应起来。
而其他鼠标消息处理函数的参数及含义与OnLButtonDown()函数完全相同。
4.2.3 鼠标消息处理实例本节将通过一个具体的实例讲解程序设计中鼠标的使用。实例实现的功能为当用户在视图窗口中按下鼠标左键,拖动鼠标时,在窗口中绘制一个随鼠标位置变化的矩形,当释放鼠标键时,停止矩形绘制。具体开发过程如下。
81
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息表4.5 MFC封装的鼠标消息及处理函数鼠标消息描述
WM_LBUTTONDOWN按下鼠标左键
WM_LBUTTONUP松开鼠标左键
WM_LBUTTONDBLCLK双击鼠标左键
WM_RBUTTONDOWN按下鼠标右键
WM_RBUTTONUP松开鼠标右键
WM_RBUTTONDBLCLK双击鼠标右键
WM_MOUSEMOVE鼠标移动
WM_MOUSEWHEEL鼠标滚轮滚动表4.6 nFlags参数的可能取值及含义鼠标消息描述
MK_LBUTTON按下了鼠标的左键
MK_MBUTTON按下了鼠标的中键
MK_RBUTTON按下了鼠标的右键
MK_CONTROL按下了键盘上的【Ctrl】键
MK_SHIFT按下了键盘上的【Shift】键
WM_RBUTTONDBLCLK双击鼠标右键
1,使用MFC AppWizard创建SDI工程启动Visual C++6.0,执行“File”→“New”菜单命令,在“Project”选项卡中,创建一个MFC
AppWizard[EXE]工程,工程名为“Ch5Demo4”。在“MFC AppWizard Step 1”对话框中,选中
“Single document”,即基于单文档的MFC工程,其余的几步向导对话框均采用默认设置。
2,添加鼠标消息映射和响应函数执行“View”→“ClassWizard”菜单命令项,或者使用【Ctrl+W】快捷键,打开“MFC
ClassWizard”对话框。在对话框的“Message Maps”选项卡中的“Class name”列表框中选择要响应鼠标消息的类“CCh5Demo4View”,在“Object IDs”列表框中选择“CCh5Demo4View”,在
“Messages”列表框中选择“WM_LBUTTONDOWN”鼠标消息,如图4.10所示。
图4.10,MFC ClassWizard”对话框单击“Add Function”按钮,即在视图类
“CCh5Demo4View”中添加了消息处理函数OnL
ButtonDown()。用同样的方法,添加WM_LBUTTONUP
和On_MouseMove()消息映射和消息处理函数。
3,为CCh5Demo4View类添加成员函数在工作区窗口的“ClassView”标签栏中,右击
“CCh5Demo4View”,在弹出菜单中执行“Add
Member Function”命令,如图4.11所示。在弹出的
“Add Member Function”对话框中,输入函数的类型、
名称等信息,如图4.12所示。单击“OK”按钮,即在视图类“CCh5Demo4View”中添加一个成员函数
DrawRect()。
82
励志照亮人生 编程改变命运零基础学 Visual C++
图4.11 执行添加成员变量命令
4,手工添加实现代码
(1)在视图类CCh5Demo4View的头文件“Ch5
Demo4View.h”中,定义三个变量,记录相应的状态。代码如下:
public:
BOOL fDowned; //是否在拉动
CPoint ptDown; //鼠标左键按下位置
CPoint ptUp; //鼠标左键松开位置
(2)在CCh5Demo4View类的构造函数中,初始化变量。代码如下:
CCh5Demo4View::CCh5Demo4View()
{
// TODO,add construction code here
//初始化定义的变量
fDowned=false;
ptDown.x=0;
ptDown.y=0;
ptUp.x=0;
ptUp.y=0;
}
(3)分别添加DrawRect()、OnLButtonDown()、OnLButtonUp()和OnMouseMove()函数的实现代码。如下:
void CCh5Demo4View::DrawRect()
{
CClientDC dc(this); //获取DC
CRect rect;
GetClientRect(rect); //获取客户窗口区域
CBrush brush(RGB(255,255,255));
dc.FillRect(rect,&brush); //填充背景色为白色
dc.Rectangle(ptDown.x,ptDown.y,ptUp.x,ptUp.y); //绘制矩形
}
void CCh5Demo4View::OnLButtonDown(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
fDowned=TRUE;
ptUp=ptDown=point;
CView::OnLButtonDown(nFlags,point);
}
void CCh5Demo4View::OnLButtonUp(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
if(fDowned)
{
ptUp=point;
DrawRect(); //画新矩形
fDowned=FALSE;
}
83
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息图4.12 添加成员函数
CView::OnLButtonUp(nFlags,point);
}
void CCh5Demo4View::OnMouseMove(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
if(fDowned)
{
ptUp=point;
DrawRect(); //画新矩形
}
CView::OnMouseMove(nFlags,point);
}
编译运行程序,按下鼠标左键,拖动鼠标就可以实现绘制矩形,如图4.13所示。
图4.13 程序运行结果
4.2.4 鼠标光标的创建与使用鼠标光标(Cursor)是鼠标与用户之间的接口,它指示鼠标的位置,随鼠标移动而移动,是鼠标的屏幕映像。在Visual C++创建的应用程序中,鼠标都采用系统默认的光标,当然用户也可以通过编程使用自己的光标。光标的使用主要有两个步骤:创建光标和设置光标。
1,创建光标
Window系统提供了十几种标准光标,如IDC_APPSTARTING、IDC_ARROW、IDC_CROSS、
IDC_WAIT等等。一般情况下,使用这些光标就足以满足程序的需要。当然,用户也可以自己创建光标。
在Visual C++中,执行“Insert”→“Resource”菜单命令,在弹出的“Insert Resource”对话框中,选择“Cursor”选项,单击“New”按钮,即进入光标的编辑窗口,如图4.14所示。
在光标编辑窗口中,可以绘制需要的光标图形。在绘制窗口中,有一个热点设置按钮,在它旁边显示热点设置的坐标。单击这个按钮,在光标编辑器中出现一个十字光标,将十字中心放在需要设定的热点位置,单击鼠标左键即可,如图4.15所示。
84
励志照亮人生 编程改变命运零基础学 Visual C++
2,设置光标使用API函数SetCursor()可以设置当前使用的光标资源。如果要采用Window系统提供的标准光标资源,首先需要使用函数LoadStandardCursor()载入系统标准光标资源。如:
HCURSOR cusor=AfxGetApp()->LoadStandardCursor(IDC_SIZENS); //获取系统标准光标
SetCursor(cusor); //设置光标如果要载入用户绘制的光标资源,则需要在PreCreateWindow()函数中,通过API函数
LoadImage()函数将光标载入程序,而后在需要时通过函数SetCursor()设置光标。
首先需要声明一个HCURSOR变量,如下:
HCURSOR m_cursor;
在PreCreateWindow()函数中,将自定义的光标赋予m_cursor:
m_cursor=(HCURSOR)::LoadImage(cs.hInstance,MAKEINTRESOURCE(IDC_CURSOR1),
IMAGE_CURSOR,32,32,LR_CREATEDIBSECTION);
其中,IDC_CURSOR1为创建的光标资源的资源ID。
在需要使用该光标的时候,就可以使用下面语句调用该光标:
SetCursor(m_cursor);
技巧在实际编程中,用户需要使光标“消失”,即隐藏光标,这时候使用ShowCursor(false)语句实现。
下面在4.2.3节创建的“Ch5Demo4”实例中,实现当拖动鼠标绘制矩形时,更改鼠标光标为十字光标。此时,只需在OnMouseMove()函数中实现新的光标的创建与显示即可。代码如下:
void CCh5Demo4View::OnMouseMove(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
if(fDowned)
{
HCURSOR cusor=AfxGetApp( )->LoadStandardCursor(IDC_CROSS); //获取系统标准光标
SetCursor(cusor); //设置光标
ptUp=point;
DrawRect(); //画新矩形
85
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息图4.14,Insert Resource”对话框图4.15 编辑设计光标窗口
}
CView::OnMouseMove(nFlags,point);
}
此时,编译运行程序,当绘制矩形时,鼠标光标如图4.16所示。
4.2.5 捕捉鼠标在前面开发的“CCh5Demo4”中,当按下鼠标左键,
拖动鼠标到视图窗口以外后,再释放鼠标左键,此时释放左键的消息在CCh5Demo4View类中无法捕捉到。因此虽然已经释放了鼠标左键,但是CCh5Demo4View类中的
OnLButtonUp()函数却得不到执行,程序也就出现混乱。
这时,可以通过调用SetCapture()函数捕获鼠标,即无论鼠标在何位置,其产生的消息均能发送给捕获它的窗口。
一旦某个窗口捕获了鼠标,其他窗口将无法得到鼠标消息,因此,当窗口不再需要捕获鼠标消息时,应及时使用ReleaseCapture()函数将鼠标释放。
下面通过鼠标捕捉,完善“Ch5Demo4”程序。
此时,只需分别在OnLButtonDown()和OnLButtonUp()函数中,实现鼠标的捕捉与释放即可。
void CCh5Demo4View::OnLButtonDown(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
SetCapture(); //捕捉鼠标
}
void CCh5Demo4View::OnLButtonUp(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
ReleaseCapture(); //释放鼠标
}
4.2.6 锁定鼠标的作用区域在程序设计中,有时为了限制用户的某些操作,需要将鼠标的活动限定在固定的区域内,此时,
可以通过API函数ClipCursor()实现。函数原型如下:
BOOL ClipCursor(const RECT *lpRect);
其中参数*lpRect为要限定的鼠标活动区域的指针。
而通过GetClipCursor()函数则可以获取鼠标的活动有效区域,原型如下:
BOOL GetClipCursor(LPRECT lpRect);
其中参数lpRect为指向存储鼠标活动有效区域的矩形区域指针。
下面在“Ch5Demo4”绘图程序中,当按下鼠标左键时,将鼠标的活动区域限制在客户窗口内,
释放左键,恢复鼠标的活动区域。
86
励志照亮人生 编程改变命运零基础学 Visual C++
图4.16 程序运行结果此时,只需在OnLButtonDown()和OnLButtonUp()函数中添加如下代码:
void CCh5Demo4View::OnLButtonDown(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
SetCapture();//捕捉鼠标
GetClipCursor(&oldrect); //获取原鼠标活动的有效区域
CRect rect1;
GetWindowRect(&rect1); //获取客户区窗口区域
ClipCursor(&rect1); //将鼠标的移动限制的客户区
}
void CCh5Demo4View::OnLButtonUp(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
ReleaseCapture(); //释放鼠标
ClipCursor(&oldrect); //恢复鼠标的活动区域
}
其中oldrect变量在CCh5Demo4View类的头文件中定义。
public:
CRect oldrect;
编译运行程序,当按下鼠标左键并拖动鼠标时,鼠标的活动范围就被限制在客户区窗口内。
87
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息
VC常用类、控件、资源篇第4章基本输入—键盘和鼠标消息
Windows采用的是事件驱动机制,因此要让程序完成一定功能,很多情况下用户必须进行干预,
告诉程序要干什么,这就要求用户通过某种手段与计算机进行交互。鼠标和键盘是用户与Windows应用程序交换的最主要的交换设备。Windows预先定义了大量的鼠标消息和键盘消息,而MFC也对其中的常用消息进行了封装。本章主要介绍在MFC应用程序中,键盘和鼠标消息的响应与处理。
4.1 键盘消息及其处理在Windows中,键盘输入以消息的形式传递给程序的视窗消息处理程序。按下或释放一个键均会产生一条消息,系统将其放到与键盘输入窗口相对应的线程消息队列中。
4.1.1 键盘消息键盘消息有三种:键盘被按下、键盘被松开(弹起)、输入字符。其中,输入字符相当于直接得到用户输入的字符,这在不需要处理按键细节时使用;而键盘被按下、松开在按键状态改变时发送。
MFC对键盘的这三种键盘消息进行了封装,如表4.1所示。
按键和释放键消息通常是成对出现的,但如果用户按住键到一定的时间就启动了键盘的自动重复特性,系统就会产生一系列的WM_KEYDOWN消息,
在用户释放一个键时,才产生一条WM_KEYUP消息。
4.1.2 键盘消息的处理一般来讲,用户输入消息,如菜单选择、鼠标、键盘等,多在文档/视图结构的应用程序中使用。
在MFC应用程序开发中,可以使用ClassWizard为键盘消息和鼠标消息添加消息映射和消息处理函数。
在MFC文档/视图结构的应用程序中,视图窗口和框架窗口均能处理键盘和鼠标消息。当有键盘或鼠标消息产生时,系统会先将消息发往视图窗口,如果视图类中没有发现相应的消息映射和处理函数,就将该消息再发往框架窗口。所以在使用ClassWizard为键盘消息定义消息映射时,定义在视图
71
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息表4.1 MFC中封装的键盘消息键盘消息描述
WM_KEYDOWN键盘按下
WM_KEYUP键盘抬起
WM_CHAR可打印字符键按下并抬起,输入了一个字符类中就可以了。如果一个应用同时拥有多个视图,而当前活动没有对消息进行处理,则消息会发往框架窗口。
下面通过具体的实例讲解键盘消息的映射及处理。
4.1.3 按键消息的处理当按下一个键或松开一个键时,将产生一个按键消息。在MFC中,WM_KEYDOWN和
WM_KEYUP消息分别在按下一个键和松开一个键时被激发。
本节将创建一个单文档应用程序,当用户按下任意一个键时,弹出对话框显示相应的提示信息。
具体实现过程如下。
1,使用MFC AppWizard创建SDI工程启动Visual C++6.0,执行“File”→“New”菜单命令,在“Projects”选项卡中,创建一个MFC
AppWizard[exe]工程,工程名为“Ch5Demo1”,如图4.1所示。
单击“OK”按钮,在“MFC AppWizard Step 1”对话框中,选中“Single document”,即基于单文档的MFC工程,其余的几步向导对话框均采用默认设置。
2,添加WM_KEYDOWN消息映射和响应函数执行“View”→“ClassWizard”菜单命令,或者使用【Ctrl+W】快捷键,打开“MFC
ClassWizard”对话框。在“Message Maps”选项卡的“Class name”列表框中选择要响应键盘消息的类“CCh5Demo1View”,在“Object IDs”列表框中选择“CCh5Demo1View”,在“Messages”列表框中选择“WM_KEYDOWN”按键消息。
单击“Add Function”按钮,ClassWizard自动为WM_KEYDOWN添加了ON_ WM_KEYDOWN
消息映射宏和OnKeyDown()消息处理函数,如图4.2所示。
此时,在“Ch5Demo1View.cpp”资源文件的开始部分会发现自动添加的消息映射代码:
BEGIN_MESSAGE_MAP(CCh5Demo1View,CView)
//{{AFX_MSG_MAP(CCh5Demo1View)
ON_WM_KEYDOWN() //消息映射代码
//}}AFX_MSG_MAP
72
励志照亮人生 编程改变命运零基础学 Visual C++
图4.1,New”对话框图4.2,MFC ClassWizard”对话框
// Standard printing commands
END_MESSAGE_MAP()
在“Ch5Demo1View.h”头文件,自动添加了消息处理函数OnKeyDown()的声明代码如下:
protected:
//{{AFX_MSG(CCh5Demo1View)
afx_msg void OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags);
//}}AFX_MSG
3,手工添加函数的实现代码在ClassWizard对话框(图4.2)中,单击“Edit Code”按钮,在工作区中,即跳转至新增函数代码的定义处。这时,需要手工添加OnDrawcircle()函数的实现代码,如下:
void CCh5Demo1View::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
AfxMessageBox("用户按下了键!"); //弹出提示对话框
CView::OnKeyDown(nChar,nRepCnt,nFlags);
}
这里只添加了一行代码,实现弹出提示对话框。
编译运行程序后,若用户按下键盘的任意键,就会弹出提示对话框,如图4.3所示。
WM_KEYUP消息的消息映射和处理过程与
WM_KEYDOWN的完全相同。
4.1.4 按键消息处理函数从上一节实例可见,MFC会将WM_KEYDOWN消息映射为ON_WM_KEYDOWN,而对应的处理函数为
OnKeyDown()。同样,WM_KEYUP消息对应的处理函数为OnKeyUp()。它们具有完全相同的参数,声明如下:
void OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags);
void OnKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags);
参数nChar表示按键的虚拟键码。如“VK_ALT”,表示按下的键为【Alt】键,
“VK_CONTROL”表示按下的键为【Ctrl】键。Windows中常用的虚拟键码及其对应的按键如表4.2所示。
表4.2 Windows常用的虚拟键码及其对应的按键虚拟键码数值对应的键虚拟键码数值对应的键
VK_LBUTTON 1鼠标左键VK_BACK 8退格键
VK_RBUTTON 2鼠标右键VK_TAB 9制表键
VK_MBUTTON 4鼠标中键VK_RETURN 13回车键
VK_SHIFT 16 Shift键VK_CONTROL 17 Ctrl键
73
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息图4.3 程序运行结果
(续)
虚拟键码数值对应的键虚拟键码数值对应的键
VK_MENU 18 Alt键VK_PAUSE 19 Pause键
VK_CAPTIAL 20 Caplock键VK_ESCAPE 27 Esc键
VK_PRIOR 33 Page Up键VK_NEXT 34 Page Down键
VK_END 35 End键VK_HOME 36 Home键
VK_LEFT 37左箭头键VK_RIGHT 39右箭头键
VK_UP 38上箭头键VK_DOWN 40下箭头键
48?57 0?9键65?90 A?Z键
VK_SPACE 32空格键VK_SNAPSHOT 44 Print Screen键
VK_INSERT 45 Insert键VK_DELETE 46 Delete键
112?135功能键F1?F24 VK_NUMLOCK 144 NumLock键使用虚拟键码时,可以使用键码也可以直接使用其数值。如判断按键是否为回车键时,可以采用
“if(nChar == VK_RETURN)”语句,也可采用“if(nChar == 13)”语句。
说明虚拟键码是Windows内部建立的设备无关的键盘代码。在Windows中不论使用什么类型的键盘,
都将扫描代码翻译成同一的虚拟键码,这样应用程序就不用直接同硬盘硬件打交道。
参数nRepCnt表示按键的重复次数,即长时间按住一个键时,相当于按键的次数。
参数nFlags中的不同位代表不同的含义,包括键盘扫描码、扩展键盘标志、描述码、键的先前状态和转换状态标志等,一般很少用到。
根据按键消息响应函数各参数的值,可以判断按键的相关信息,便于进行灵活的程序开发。下面给出一个具体应用实例。
实例功能:当用户同时按住【Ctrl】键和【F8】键时,激发某一操作,即弹出提示对话框。实现过程如下。
1,使用MFC AppWizard创建SDI工程按照4.1.3节介绍的“Ch5Demo1”工程的创建过程,创建MFC SDI工程“Ch5Demo2”。
2,添加WM_KEYDOWN和WM_UP消息映射和响应函数同样,利用ClassWizard,分别为视图类CCh5Demo2View添加WM_KEYDOWN、WM_UP消息映射和消息处理函数。
3,手工添加实现代码
(1)在视图类CCh5Demo2View的头文件“Ch5Demo2View.h”中,定义两个布尔变量,分别用于记录【Ctrl】键和【F8】键的状态:按下时为“true”,释放后为“false”。代码如下:
public:
BOOL b_Ctrldown,b_F8down;
(2)在CCh5Demo2View类的构造函数中,初始化变量为“false”。代码如下:
CCh5Demo2View::CCh5Demo2View()
74
励志照亮人生 编程改变命运零基础学 Visual C++
{
// TODO,add construction code here
b_Ctrldown=false; //初始化
b_F8down=false; //初始化
}
(3)在WM_KEYDOWN消息响应函数onkeyDown()中,实现按键状态的赋值,并在满足条件的情况下,弹出提示对话框。代码如下:
void CCh5Demo2View::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
if(nChar==VK_CONTROL) //如果按下了【Ctrl】键
{
b_Ctrldown=true;
}
if(nChar==VK_F8) //如果按下了【F8】键
{
b_F8down=true;
}
if(b_Ctrldown&&b_F8down) //同时按下了【Ctrl】键和【F8】键
{
b_Ctrldown=false;
b_F8down=false;
AfxMessageBox("同时按下了Ctrl键和F8键"); //弹出提示框
}
CView::OnKeyDown(nChar,nRepCnt,nFlags);
}
(4)在WM_KEYUP消息响应函数Onkeyup()中,实现按键状态的赋值。代码如下:
void CCh5Demo2View::OnKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
if(nChar==VK_CONTROL) //如果释放了【Ctrl】键
{
b_Ctrldown=false;
}
if(nChar==VK_F8) //如果释放了【F8】键
{
b_F8down=false;
}
CView::OnKeyUp(nChar,nRepCnt,nFlags);
}
编译运行程序后,若用户同时按下了【Ctrl】键和【F8】键,就会弹出提示对话框,如图4.4
所示。
75
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息图4.4 程序运行结果
4.1.5 字符消息的处理当按下一个可显示的字符(数字、字母、标点等)的键时,除了产生按下键消息
WM_KEYDOWN外,还将产生一个字符消息WM_CHAR。当然,如果释放该键,还将产生
WM_KEYUP消息。用户敲击一个一个字符键后,其产生的消息及处理函数的执行顺序如图4.5所示。
图4.5 键盘消息的处理过程
MFC将WM_CHAR消息映射为ON_WM_CHAR,其对应的处理函数为OnChar()。函数OnChar()
的定义如下:
void OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)
其中参数nRepCnt和nFlags的含义与4.1.3节介绍的OnKeyDown函数的完全相同。而参数nChar存放的不再是按键的虚拟键码,而是Windows字符集的字符代码,默认为ASCII码。键盘上常见的字符及其ASCII码值如表4.3所示。
表4.3 常见的字符及其ASCII码值字符符号ASCII码值字符符号ASCII码值字符符号ASCII码值
!33 "34 # 35
$36 % 7 & 8
'39 (40 ) 41
*42 + 3,
-45,46 / 47
76
励志照亮人生 编程改变命运零基础学 Visual C++
E
用户敲击字符按键处理函数键盘消息
(续)
字符符号ASCII码值字符符号ASCII码值字符符号ASCII码值
0~9 48~57,58 ; 59
<60 =61 > 62
63 @ 64 A~Z 65~90
[91 \92 ] 93
^94 _ 5 ` 6
a~z 97~122 { 123 | 124
} 125 ~ 126
如果nChar取值为64,则表示按键字符为“@”。下面给出一个在视图窗口中,显示键盘输入字符的实例。
该实例实现的功能是在单文档应用程序界面中,当用户通过键盘键入字符时,在视图窗口依次显示键入的字符。当用户按下【Enter】键时,进行换行输出。具体实现过程如下。
1,使用MFC AppWizard创建SDI工程按照4.1.3节介绍的“Ch5Demo1”工程的创建过程,创建MFC SDI工程“Ch5Demo3”。
2,添加WM_CHAR消息映射和响应函数执行“View”→“ClassWizard”菜单命令,或者使用【Ctrl+W】快捷键,打开“MFC
ClassWizard”对话框。在“Message Maps”选项卡的“Class name”列表框中选择要响应键盘消息的类“CCh5Demo3View”,在“Object IDs”列表框中选择“CCh5Demo3View”,在“Messages”列表框中选择“WM_CHAR”字符按键消息。
单击“Add Function”按钮,ClassWizard自动为WM_CHAR添加了ON_ WM_CHAR消息映射宏和OnChar()消息处理函数,如图4.6所示。
图4.6,MFC ClassWizard”对话框
77
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息
3,手工添加实现代码
(1)在视图类CCh5Demo3View的头文件“Ch5Demo3View.h”中,定义CPoint型变量,用于记录字符在视图窗口中的输出位置。代码如下:
public:
CPoint ptCharacter; //记录字符位置
(2)在CCh5Demo3View类的构造函数中,初始化ptCharacter位置为(0,0)。代码如下:
CCh5Demo3View::CCh5Demo3View()
{
// TODO,add construction code here
//初始位置设置在(0,0)
ptCharacter.x=0;
ptCharacter.y=0;
}
(3)在WM_CHAR消息响应函数OnChar()中,实现字符的显示以及换行。代码如下:
void CCh5Demo3View::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
if(nChar==13) //按下了回车键
{
//换行
ptCharacter.x=0;
ptCharacter.y=ptCharacter.y+25;
}
else
{
CClientDC dc(this);
dc.TextOut(ptCharacter.x,ptCharacter.y,(LPCTSTR)&nChar); //输出显示字符
CSize textsize;
textsize=dc.GetTextExtent((LPCTSTR)&nChar); //获取当前字符大小
//前进到下一个字符位置
ptCharacter.x=ptCharacter.x+textsize.cx;
}
CView::OnChar(nChar,nRepCnt,nFlags);
}
编译运行程序后,当用户通过键盘键入字符时,即会在视图窗口输出显示。如果按下了【Ente】键,将换行输出,如图4.7所示。
说明本程序只是简单演示了WM_CHAR消息响应和按键字符的显示操作,并没有实现窗口的重绘。另外,实际程序如果涉及文本输入、编辑操作,则是通过Edit控件和CEditView视图来实现的。
78
励志照亮人生 编程改变命运零基础学 Visual C++
图4.7 程序运行结果
4.1.6 创建键盘插入符键盘插入符(Caret)是一个闪烁的位图(通常是一个细的垂直杠),它可使用户知道在窗口何处可进行有效的键盘输入。
CWnd类提供了8个创建和管理键盘插入符的成员函数,如表4.4所示。
Windows总是把键盘消息送到拥有输入焦点的窗口。一般情况下,一个应用程序有多个窗口,而键盘消息只能被一个窗口接收,接收键盘消息的窗口称为有“输入焦点”的窗口,具有输入焦点的窗口称为活动窗口。当某一个窗口成为活动窗口时,
Windows会加亮显示其标题栏或窗口边框。
Windows用WM_SETFOCUS和WM_
KILLFOCUS消息通知即将通知接收或失去输入焦点的窗口。MFC分别对这两个消息进行了封装。
在窗口获得键盘焦点时,就可以创建插入符了,若窗口没有焦点,就不能进行键盘输入。另外,
插入符一旦创建起来,还要在窗口中对其进行定位和显示。下面通过具体的实例讲解插入符的使用。
下面在4.1.5节创建的“CCh5Demo3”实例的基础上,在字符将要显示的位置显示插入符。其具体实现过程如下。
1,打开工程启动Visual C++ 6.0,打开4.1.5节创建的“Ch5Demo3”工程文件“Ch5Demo3.dsw”。
2,添加WM_SETFOCUS消息映射和响应函数执行“View”→“ClassWizard”菜单命令,
或者使用【Ctrl+W】快捷键,打开“MFC
ClassWizard”对话框。在“Message Maps”选项卡中的“Class name”列表框中选择要响应捕获窗口消息的类“CCh5Demo3View”,在
“Object IDs”列表框中选择“CCh5Demo3View”,
在“Messages”列表框中选择“WM_
SETFOCUS”捕获窗口消息。
单击“Add Function”按钮,ClassWizard自动为WM_SETFOCUS添加了ON_
WM_SETFOCUS消息映射宏和OnSetFocus()消息处理函数,如图4.8所示。
3,手工添加实现代码
(1)在WM_SETFOCUS消息响应函数OnSetFocus()中,实现插入符的创建与显示。代码如下:
void CCh5Demo3View::OnSetFocus(CWnd* pOldWnd)
79
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息表4.4 创建和管理键盘插入符的CWnd成员函数函数功能
CreateCaret使用用户提供的位图创建插入符
CreateGrayCaret创建用户定义大小的实心灰色插入符
CreateSolidCaret创建用户定义大小的实心黑色插入符
DestoryCaret销毁插入符
ShowCaret显示插入符
HideCaret隐藏插入符
GetCaretPos返回插入符的位置
SetCaretPos移动插入符到窗口的某一位置图4.8,MFC ClassWizard”对话框
{
CView::OnSetFocus(pOldWnd);
// TODO,Add your message handler code here
CreateSolidCaret(4,20); //创建插入符
SetCaretPos (ptCharacter); //将插入符移到当前字符输入点
ShowCaret (); //显示插入符
}
(2)修改OnChar()函数,在适当的时机显示、隐藏插入符。代码如下:
void CCh5Demo3View::OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)
{
// TODO,Add your message handler code here and/or call default
if(nChar==13) //按下了回车键
{
//换行
ptCharacter.x=0;
ptCharacter.y=ptCharacter.y+25;
SetCaretPos (ptCharacter); //将插入符移到键入点
ShowCaret (); //显示插入符
}
else
{
CClientDC dc(this);
HideCaret (); //隐藏插入符
dc.TextOut(ptCharacter.x,ptCharacter.y,(LPCTSTR)&nChar);//显示字符
CSize textsize;
textsize=dc.GetTextExtent((LPCTSTR)&nChar); //获取当前字符大小
//前进到下一个字符位置
ptCharacter.x=ptCharacter.x+textsize.cx;
SetCaretPos (ptCharacter); //将插入符移到键入点
ShowCaret (); //显示插入符
}
CView::OnChar(nChar,nRepCnt,nFlags);
}
编译运行程序后,会发现与原程序相比,在字符的输入位置增加了插入符,如图4.9所示。
图4.9 程序运行结果
80
励志照亮人生 编程改变命运零基础学 Visual C++
4.2 鼠标消息及其处理鼠标消息是应用程序开发中常需要处理的消息,
当鼠标移动、左键(右键)的按下或者松开、双击操作等都可以产生相应的鼠标消息。
4.2.1 鼠标消息在Windows中,鼠标消息分为两类:客户区鼠标消息和非客户区鼠标消息,其中包含了大量的鼠标消息。而在MFC中,只对其中常用的几种鼠标消息进行了封装,如表4.5所示。利用ClassWizard可以轻松地为这些消息添加消息映射和消息处理函数。
4.2.2 鼠标消息处理函数与键盘消息相同,当使用ClassWizard为鼠标消息添加消息映射时,系统自动为其添加了处理函数。
消息处理函数就是在消息名前去除“WM_”前缀,
换成“on”前缀,如对消息WM_LBUTTONDOWN,
消息映射宏的处理函数为OnLButtonDown(),其声明如下:
afx_msg void OnLButtonDown(UINT nFlags,CPoint point );
参数nFlags表明了当前一些按键的消息,其可取值如表4.6所示。
可以通过“位与”操作进行相关检测。在实际编程中,常使用nFlags参数指出消息生成时的鼠标键以及【Shift】键和【Ctrl】键的状态,如当按下鼠标左键时,同时检测【Shift】键和【Ctrl】键的状态,
可采用下面的代码:
void OnLButtonDown(UINT nFlags,CPoint point ) //按下了鼠标左键
{
if((nFlags &MK_CONTROL)&&( nFlags &MK_SHIFT)) //【Shift】和【Ctrl】键都被按下
...
}
参数point表示当前鼠标的设备坐标,坐标原点对应屏幕左上角。
通过point参数可以将鼠标操作与屏幕显示对应起来。
而其他鼠标消息处理函数的参数及含义与OnLButtonDown()函数完全相同。
4.2.3 鼠标消息处理实例本节将通过一个具体的实例讲解程序设计中鼠标的使用。实例实现的功能为当用户在视图窗口中按下鼠标左键,拖动鼠标时,在窗口中绘制一个随鼠标位置变化的矩形,当释放鼠标键时,停止矩形绘制。具体开发过程如下。
81
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息表4.5 MFC封装的鼠标消息及处理函数鼠标消息描述
WM_LBUTTONDOWN按下鼠标左键
WM_LBUTTONUP松开鼠标左键
WM_LBUTTONDBLCLK双击鼠标左键
WM_RBUTTONDOWN按下鼠标右键
WM_RBUTTONUP松开鼠标右键
WM_RBUTTONDBLCLK双击鼠标右键
WM_MOUSEMOVE鼠标移动
WM_MOUSEWHEEL鼠标滚轮滚动表4.6 nFlags参数的可能取值及含义鼠标消息描述
MK_LBUTTON按下了鼠标的左键
MK_MBUTTON按下了鼠标的中键
MK_RBUTTON按下了鼠标的右键
MK_CONTROL按下了键盘上的【Ctrl】键
MK_SHIFT按下了键盘上的【Shift】键
WM_RBUTTONDBLCLK双击鼠标右键
1,使用MFC AppWizard创建SDI工程启动Visual C++6.0,执行“File”→“New”菜单命令,在“Project”选项卡中,创建一个MFC
AppWizard[EXE]工程,工程名为“Ch5Demo4”。在“MFC AppWizard Step 1”对话框中,选中
“Single document”,即基于单文档的MFC工程,其余的几步向导对话框均采用默认设置。
2,添加鼠标消息映射和响应函数执行“View”→“ClassWizard”菜单命令项,或者使用【Ctrl+W】快捷键,打开“MFC
ClassWizard”对话框。在对话框的“Message Maps”选项卡中的“Class name”列表框中选择要响应鼠标消息的类“CCh5Demo4View”,在“Object IDs”列表框中选择“CCh5Demo4View”,在
“Messages”列表框中选择“WM_LBUTTONDOWN”鼠标消息,如图4.10所示。
图4.10,MFC ClassWizard”对话框单击“Add Function”按钮,即在视图类
“CCh5Demo4View”中添加了消息处理函数OnL
ButtonDown()。用同样的方法,添加WM_LBUTTONUP
和On_MouseMove()消息映射和消息处理函数。
3,为CCh5Demo4View类添加成员函数在工作区窗口的“ClassView”标签栏中,右击
“CCh5Demo4View”,在弹出菜单中执行“Add
Member Function”命令,如图4.11所示。在弹出的
“Add Member Function”对话框中,输入函数的类型、
名称等信息,如图4.12所示。单击“OK”按钮,即在视图类“CCh5Demo4View”中添加一个成员函数
DrawRect()。
82
励志照亮人生 编程改变命运零基础学 Visual C++
图4.11 执行添加成员变量命令
4,手工添加实现代码
(1)在视图类CCh5Demo4View的头文件“Ch5
Demo4View.h”中,定义三个变量,记录相应的状态。代码如下:
public:
BOOL fDowned; //是否在拉动
CPoint ptDown; //鼠标左键按下位置
CPoint ptUp; //鼠标左键松开位置
(2)在CCh5Demo4View类的构造函数中,初始化变量。代码如下:
CCh5Demo4View::CCh5Demo4View()
{
// TODO,add construction code here
//初始化定义的变量
fDowned=false;
ptDown.x=0;
ptDown.y=0;
ptUp.x=0;
ptUp.y=0;
}
(3)分别添加DrawRect()、OnLButtonDown()、OnLButtonUp()和OnMouseMove()函数的实现代码。如下:
void CCh5Demo4View::DrawRect()
{
CClientDC dc(this); //获取DC
CRect rect;
GetClientRect(rect); //获取客户窗口区域
CBrush brush(RGB(255,255,255));
dc.FillRect(rect,&brush); //填充背景色为白色
dc.Rectangle(ptDown.x,ptDown.y,ptUp.x,ptUp.y); //绘制矩形
}
void CCh5Demo4View::OnLButtonDown(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
fDowned=TRUE;
ptUp=ptDown=point;
CView::OnLButtonDown(nFlags,point);
}
void CCh5Demo4View::OnLButtonUp(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
if(fDowned)
{
ptUp=point;
DrawRect(); //画新矩形
fDowned=FALSE;
}
83
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息图4.12 添加成员函数
CView::OnLButtonUp(nFlags,point);
}
void CCh5Demo4View::OnMouseMove(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
if(fDowned)
{
ptUp=point;
DrawRect(); //画新矩形
}
CView::OnMouseMove(nFlags,point);
}
编译运行程序,按下鼠标左键,拖动鼠标就可以实现绘制矩形,如图4.13所示。
图4.13 程序运行结果
4.2.4 鼠标光标的创建与使用鼠标光标(Cursor)是鼠标与用户之间的接口,它指示鼠标的位置,随鼠标移动而移动,是鼠标的屏幕映像。在Visual C++创建的应用程序中,鼠标都采用系统默认的光标,当然用户也可以通过编程使用自己的光标。光标的使用主要有两个步骤:创建光标和设置光标。
1,创建光标
Window系统提供了十几种标准光标,如IDC_APPSTARTING、IDC_ARROW、IDC_CROSS、
IDC_WAIT等等。一般情况下,使用这些光标就足以满足程序的需要。当然,用户也可以自己创建光标。
在Visual C++中,执行“Insert”→“Resource”菜单命令,在弹出的“Insert Resource”对话框中,选择“Cursor”选项,单击“New”按钮,即进入光标的编辑窗口,如图4.14所示。
在光标编辑窗口中,可以绘制需要的光标图形。在绘制窗口中,有一个热点设置按钮,在它旁边显示热点设置的坐标。单击这个按钮,在光标编辑器中出现一个十字光标,将十字中心放在需要设定的热点位置,单击鼠标左键即可,如图4.15所示。
84
励志照亮人生 编程改变命运零基础学 Visual C++
2,设置光标使用API函数SetCursor()可以设置当前使用的光标资源。如果要采用Window系统提供的标准光标资源,首先需要使用函数LoadStandardCursor()载入系统标准光标资源。如:
HCURSOR cusor=AfxGetApp()->LoadStandardCursor(IDC_SIZENS); //获取系统标准光标
SetCursor(cusor); //设置光标如果要载入用户绘制的光标资源,则需要在PreCreateWindow()函数中,通过API函数
LoadImage()函数将光标载入程序,而后在需要时通过函数SetCursor()设置光标。
首先需要声明一个HCURSOR变量,如下:
HCURSOR m_cursor;
在PreCreateWindow()函数中,将自定义的光标赋予m_cursor:
m_cursor=(HCURSOR)::LoadImage(cs.hInstance,MAKEINTRESOURCE(IDC_CURSOR1),
IMAGE_CURSOR,32,32,LR_CREATEDIBSECTION);
其中,IDC_CURSOR1为创建的光标资源的资源ID。
在需要使用该光标的时候,就可以使用下面语句调用该光标:
SetCursor(m_cursor);
技巧在实际编程中,用户需要使光标“消失”,即隐藏光标,这时候使用ShowCursor(false)语句实现。
下面在4.2.3节创建的“Ch5Demo4”实例中,实现当拖动鼠标绘制矩形时,更改鼠标光标为十字光标。此时,只需在OnMouseMove()函数中实现新的光标的创建与显示即可。代码如下:
void CCh5Demo4View::OnMouseMove(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
if(fDowned)
{
HCURSOR cusor=AfxGetApp( )->LoadStandardCursor(IDC_CROSS); //获取系统标准光标
SetCursor(cusor); //设置光标
ptUp=point;
DrawRect(); //画新矩形
85
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息图4.14,Insert Resource”对话框图4.15 编辑设计光标窗口
}
CView::OnMouseMove(nFlags,point);
}
此时,编译运行程序,当绘制矩形时,鼠标光标如图4.16所示。
4.2.5 捕捉鼠标在前面开发的“CCh5Demo4”中,当按下鼠标左键,
拖动鼠标到视图窗口以外后,再释放鼠标左键,此时释放左键的消息在CCh5Demo4View类中无法捕捉到。因此虽然已经释放了鼠标左键,但是CCh5Demo4View类中的
OnLButtonUp()函数却得不到执行,程序也就出现混乱。
这时,可以通过调用SetCapture()函数捕获鼠标,即无论鼠标在何位置,其产生的消息均能发送给捕获它的窗口。
一旦某个窗口捕获了鼠标,其他窗口将无法得到鼠标消息,因此,当窗口不再需要捕获鼠标消息时,应及时使用ReleaseCapture()函数将鼠标释放。
下面通过鼠标捕捉,完善“Ch5Demo4”程序。
此时,只需分别在OnLButtonDown()和OnLButtonUp()函数中,实现鼠标的捕捉与释放即可。
void CCh5Demo4View::OnLButtonDown(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
SetCapture(); //捕捉鼠标
}
void CCh5Demo4View::OnLButtonUp(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
ReleaseCapture(); //释放鼠标
}
4.2.6 锁定鼠标的作用区域在程序设计中,有时为了限制用户的某些操作,需要将鼠标的活动限定在固定的区域内,此时,
可以通过API函数ClipCursor()实现。函数原型如下:
BOOL ClipCursor(const RECT *lpRect);
其中参数*lpRect为要限定的鼠标活动区域的指针。
而通过GetClipCursor()函数则可以获取鼠标的活动有效区域,原型如下:
BOOL GetClipCursor(LPRECT lpRect);
其中参数lpRect为指向存储鼠标活动有效区域的矩形区域指针。
下面在“Ch5Demo4”绘图程序中,当按下鼠标左键时,将鼠标的活动区域限制在客户窗口内,
释放左键,恢复鼠标的活动区域。
86
励志照亮人生 编程改变命运零基础学 Visual C++
图4.16 程序运行结果此时,只需在OnLButtonDown()和OnLButtonUp()函数中添加如下代码:
void CCh5Demo4View::OnLButtonDown(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
SetCapture();//捕捉鼠标
GetClipCursor(&oldrect); //获取原鼠标活动的有效区域
CRect rect1;
GetWindowRect(&rect1); //获取客户区窗口区域
ClipCursor(&rect1); //将鼠标的移动限制的客户区
}
void CCh5Demo4View::OnLButtonUp(UINT nFlags,CPoint point)
{
// TODO,Add your message handler code here and/or call default
ReleaseCapture(); //释放鼠标
ClipCursor(&oldrect); //恢复鼠标的活动区域
}
其中oldrect变量在CCh5Demo4View类的头文件中定义。
public:
CRect oldrect;
编译运行程序,当按下鼠标左键并拖动鼠标时,鼠标的活动范围就被限制在客户区窗口内。
87
励志照亮人生 编程改变命运第5 章基本输入—键盘和鼠标消息