第 4 章 基本输入处理
81
第 4章 基本输入处理
在 Windows 事件驱动的运行机制下,想让程序完成一定功能,很多情况下必须用户进行干
预,告诉程序要干什么,这就要求用户通过某种手段与计算机进行交互。鼠标和键盘是用户与
Windows 应用程序交换的最主要的交换设备。鼠标和键盘的输入也被 Windows 纳入事件驱动的
机制,被称为鼠标事件和键盘事件。而且,Windows 已预先定义了大量的鼠标消息和键盘消息。
本章将重点介绍鼠标和键盘的消息及其处理方法,另外,在本章的开始首先介绍 C++中的 I/O
流处理。
4.1 C++中的 I/O 流处理
C++的输出操作将一个对象的状态转换成一个字符序列,输出到某个地方。输入操作也
是从某个地方接收到一个字符序列,然后将其转换成
一个对象的状态所要求的格式。在 C++中,将数据从
一个对象到另一个对象的流动抽象为“流”。流动的方
向不同,构成输入/输出流,即 I/O 流。与输入设备(键
盘)相联系的流称为输入流,与输出设备(屏幕)相
联系的流称为输出流。数据的输入、输出操作是通过
I/O 流来实现的。数据的输入、输出操作可
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示为图
4.1 所示。
4.1.1 iostream流类库
C++将与输入和输出有关的操作定义为一个类体系,放在一个系统库里,以备用户调用。
执行输入和输出操作的类体系就叫做流类,提供这个流类实现的系统库就叫做流类库。C++中
提供的流类库为“iostream”。
iostream 库中包含输入流(istream 类)、输出流(ostream)类和可同时处理输入输出的流
(iostream 类)。输入/输出流类的继承层次结构如图 4.2 所示。
iostream 库中含有 4 个预定义的流。
□ cin:
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
输入流,处理标准输入。
□ cout:标准输出流,处理标准输出。
□ cerr:非缓冲标准错误流,向标准错误设备发出非缓冲输出。
□ clog:经缓冲标准错误流。
计算机内存 计算机外设
字符序列
输出操作
输入操作
图 4.1 数据的输入、输出操作
Visual C++ 6.0 开发指南
82
iostream
istream ostream
ios streambuf
一个指针成员指
向该类对象
保存流的状态变
量并处理错误 实现缓冲区
图 4.2 iostream流类库的层次结构
4.1.2 非格式化输入/输出
非格式化输入/输出就是按系统预定义的格式进行的输入/输出。按默认约定,每个 C++程
序都能使用标准 I/O 流,如标准输入、标准输出。cin 用于处理标准输入,即键盘输入,cout
用于处理标准输出,即屏幕输出,它们被定义在 iostream.h 头文件中。
说明:在使用 cout和 cin前,要用编译预处理命令将所使用的头文件“iostream.h”包含到
源程序中。
1.非格式化输入
标准输入对象 cin 是 istream流类的对象,cin 在 iostream.h 头文件中是作为全局对象定义的,
如下。
istream cin(stdin);
其中,stdin 表示标准输入设备名,即键盘。
“>>”是预定义的提取运算符,作用在流类对象 cin 上,实现默认格式的键盘输入。使用
cin 将数据输入到变量的格式如下。
cin>>V1>>V2>>…>>Vn;
其中,V1、V2、…、Vn 都是变量。功能是暂停执行程序,等待用户从键盘输入数据,各
数据间用空格或 Tab 键分隔,输入数据类型要与接受变量类型一致。输完后,按回车键结束。
另外,istream 类中还提供了一些输入相关函数,常用的函数及其功能如表 4-1 所示。
表 4-1 istream类提供的常用的输入函数
函 数 名 称 函 数 功 能
get( ) 从输入流中,读一个字符,可读取输入的空格
getline() 从输入流中读取一行字符
read() 从输入流中读取一定数量的字符
ignore() 从输入流中跳过指定数量的字符
第 4 章 基本输入处理
83
续表
函 数 名 称 函 数 功 能
putback() 向输入流中插入特定的字符
peek() 返回输入流中下一个要读取的字符
gcount() 统计输入字符的个数
2.非格式化输出
标准输出对象 cout 是 ostream 流类的对象,它在 iostream.h 头文件中是作为全局对象定义
的,如下。
ostream cout(stdout);
其中,stdout 表示标准输出设备名,即屏幕。
“<<”是预定义的插入运算符,作用在流类对象 cout 上,实现默认格式的屏幕输出。使用
cout 输出表达式值到屏幕上的格式如下。
cout<
using namespace std;
int main()
{
int getnum(char *); //获取数字字符函数
char buf[100];
cout << "请输入字符"<< endl;
while (getnum(buf))
{
cout << "其中的数字为:";
cout << buf << endl;
}
return 0;
}
int getnum(char *s) //获取数字字符函数实现
{
*s = '\0';
char ch;
while (cin.get(ch) && !cin.eof() && !isdigit(ch)) ; //去掉前面的非数字字符
do
*s++ = ch; //读入数字串
while (cin.get(ch) && !cin.eof() && isdigit(ch)); //如果为数字字符
*s = '\0';
if(!cin.eof())
cin.putback(ch); //将字符放回原位置
if(!cin || cin.eof())
return 0;
return 1;
}
第 4 章 基本输入处理
85
(3)编译运行工程:利用快捷键“F5”编译运行工程。根据窗口的信息提示输入字符串,
输入完毕,按回车后,程序就会提取其中的数字,并显示出来,最终得到结果如图 4.4 所示。
图 4.4 程序运行结果
3.程序要点
程序中语句:“while (cin.get(ch) && !cin.eof() && !isdigit(ch));”用于等待输入符合条件的字
符,并且在输入 Ctrl_z 后终止程序运行。
其中函数 isdigit()是 C++标准库中的函数,当 ch 是数字字符时,该函数返回 True。而 eof()
函数为 basic_ios 类中的成员函数,用于判断是否达到字符串流的末尾。
4.1.4 格式化输入/输出
格式化输入/输出主要包括控制状态标志、输出宽度、填充字符、输出精度等内容。其目的
是实现特定的输出、输出格式。其实现方式有两种:使用状态标志和成员函数进行格式化输出
和使用流操作符进行格式输出。
1.使用状态标志和成员函数进行格式化输出
输入、输出格式由各种状态标志来确定,它们是定义在 ios 类中的枚举量,引用时必须包
含 ios::前缀。常用的状态标志及其含义如表 4-3 所示。
表 4-3 状态标志及其含义
标 志 名 称 标 志 含 义
ios::dec 整数十进制表示
ios::oct 整数八进制表示
ios::hex 整数十六进制表示
ios::left 左对齐输出
ios::right 右对齐输出
ios:: internal 居中对齐输出
ios:: scientific 采用科学计数法表示浮点数
ios:: fixed 浮点数十进制表示
ios::showpoint 带小数点的浮点输出
ios::showpos 在显示正数时,显示一个“+”号
ios::uppercase 以大写字母输出,即十六进制数值输出使用大写 A~F,科学计数显示使用大写字母 E
ios::skipws 跳过输入的空格
Visual C++ 6.0 开发指南
86
而这些状态标志的设置是通过 ios 类相关的成员函数实现的。ios 类中,与此相关共有 3 个
重要成员函数。
(1)设置状态标志函数 setf()
setf 函数用于设置状态标志,其一般格式如下:
long ios::setf(long flags)
(2)清除状态标志函数 unsetf()
unsetf 函数用于清除状态标志,其一般格式如下:
long ios::unsetf(long flags)
(3)或取状态标志函数 flaps()
函数 flaps 用于获取状态标志,有两种形式,其格式分别如下:
long ios:: flags()
long ios::flags(long flag)
说明:其中参数 flag即为表 4-3所示的标志符,也可以他们的组合。
另外,在 ios 类中,还提供了一些控制输出宽度、填充字符、输出精度等格式的成员函数,
分别介绍如下。
(1)输出宽度控制函数 width()
设置输出宽度函数 width()有两种形式,其格式分别如下:
int ios::width(int len)
int ios::width()
其中,第一种形式是设置输出宽度,并返回原来的输出宽度;第二种形式是返回当前输出
宽度,输出宽度为 0。
(2)设置填充字符函数 fill()
填充字符的作用是当输出值不足输出宽度时用填充字符来填充,默认填充字符为空格。它
与 width()函数配合使用,否则没有意义。
设置填充字符函数 fill()有两种形式,其格式分别如下:
char ios::fill(char ch)
char ios::fill()
第一种形式是重新设置填充字符,并返回设置前的填充字符;第二种形式是返回当前的填
充字符。
(3)设置输出精度函数 precision()
设置浮点数输出精度函数 precision()有两种形式,其格式分别如下:
第 4 章 基本输入处理
87
int ios::precision(int p)
int ios::precision()
第一种形式是重新设置输出精度,并返回设置前的输出精度;第二种形式是返回当前的输
出精度。
2.用流操作符进行格式输出
为了不直接以标志位的方式去处理流的状态,C++标准库提供了标准的操作符函数专门操
控这些状态。这组函数不属于任何类成员,定义在“iomanip.h”头文件中。这些操作符用在提
取运算符“>>”或插入运算符“<<”后面来设定输入/输出格式,即在读写对象之间插入一个
修改状态的操作。常用的流操作符及其含义如表 4-4 所示。
表 4-4 流操作符及其含义
操作符名称 操作符含义
dec 转换基数为十进制形式
oct 转换基数为八进制形式
hex 转换基数为十六进制形式
left 左对齐输出
right 右对齐输出
internal 居中对齐输出
scientific 采用科学计数法表示浮点数
fixed 浮点数十进制表示
showpoint 带小数点的浮点输出
showpos 在显示正数时,显示一个“+”号
uppercase 以大写字母输出,即十六进制数值输出使用大写 A~F,科学计数显示使用大写字母 E
setw() 设置输入/输出宽度
setfill() 设置输出填充字符
setprecision() 设置输出精度函数
setbase() 设置进制数
endl 控制换行
ends 代表输出单字符“\0”
4.1.5 格式化输入/输出实例
上节已经讲过,格式化输入/输出有两种实现方式:使用状态标志和成员函数进行格式化输
出和使用流操作符进行格式输出。本节分别针对这两种方式给出两个简单示例。
1.使用状态标志和成员函数进行格式化输出实例
实例代码如下:
Visual C++ 6.0 开发指南
88
#include
using namespace std;
void main()
{
cout<<"《VISUAL C++大全》:";
cout.width(5); //设置单价 78.5显示宽度为 5个字符
cout.fill(‘*’); //宽度不满用*字符填充
cout<<78.5<<“ ”; //按照上面的格式设置输出
cout.setf(ios::scientific); //用科学记数法输出销售额
cout.precision(3); //保留 3位精度
cout<<25835.5<<“ ”;
//显示库存数量,带有正负号、左对齐
cout.setf(ios::showpos|ios::left);
cout<<1435<
#include
using namespace std;
int main()
{
cout<<"使用流操作符进行格式输出"<
记录
混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载
键按下的次数
16~23位 键盘的扫描码,各厂商可能不同
24位 扩展键盘标志
25~26位 未用
27~28位 Windows系统保留
29位 描述码,当 ALT键按下时为 1,其他为 0
30位 键的先前状态,如果键在此之前就是被按下的,此位为 1,如果是松开的,此位为 0
31位 转换状态,当键按下时,此位为 0,松开时此位为 1
MFC 会将 WM_KEYDOWN 消息映像为 ON_WM_KEYDOWN(),而相应的处理函数为
OnKeyDown(),声明如下。
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
其中各参数的含义如下。
□ nChar:为消息中的 wParam 参数,即虚键码。
□ nRepCnt:为 lParam 参数的 0~15 位,即按键的重复次数。
□ nFlags:为 lParam 参数的 18~31 位,包括键盘扫描码、扩展键盘标志、描述码、键的
先前状态和转换状态标志。
键盘抬起消息 WM_KEYUP 与此相似,这里不再详细介绍。
4.2.3 字符消息及其处理
为了全面了解键盘字符消息所包含的信息,下面剖析 WM_CHAR 消息。WM_CHAR 消息
是在 WM_KEYDOWN 消息之后发送到应用程序窗口的。
□ WM_CHAR 消息的 wParam 参数中存放的是字符代码,该字符代码是根据
WM_KEYDOWN 消息提供的虚键码,由 TranslateMessage 函数编译而成的 Windows 字
符集的字符代码。
□ WM_CHAR 消息的 lParam 参数存放的信息与 WM_KEYDOWN 消息的 lParam 参数完
全相同。
字符集是计算机对物理键盘上字符的映射集。早期的计算机只支持单字符集,它是由 8 位
单字符来表示字符集,这个集最多可以包括 255 个字符,这对于西文、字母拼音类语言足够了。
单字符集中最常用的就是 ANSI 字符集,TranslateMessage 函数就是将 WM_KEYDOWN 消息中
得到的虚键值翻译成 ANSI 字符集。
此外,还有多字节字符集。多字节字符集主要支持非西方语言,特别是非拼读语言。其中
最著名的是支持亚洲语言的双字节集(DBCS)和 Win32 支持的 Unicode 字符编码标准。字符
集问题还涉及其他一些复杂技术,这里不详细介绍。
Visual C++ 6.0 开发指南
92
4.2.4 键盘输入的其他相关编程技术
除了按键消息和字符消息外,与键盘编程相关的还有其他一些技术,简单介绍如下。
1.输入焦点窗口
Windows 总是把键盘消息送到拥有输入焦点的窗口。一般情况下,一个应用程序有多个窗
口,而键盘消息只能被一个窗口接收。接收键盘消息的窗口称为有“输入焦点”的窗口,具有
输入焦点的窗口称为活动窗口。当某一个窗口成为活动窗口时,Windows 会加亮显示其标题栏
或窗口边框。
Windows 用 WM_SETFOCUS 和 WM_KILLFOCUS 消息通知即将接收或失去输入焦点的窗
口。而 CWnd::SetFocus( )函数可把输入焦点转移到另一个窗口,CWnd::GetFocus( )函数则找到
当前用于输入焦点的窗口,它的返回类型为 CWnd。
2.插入符
和鼠标与光标关联一样,与键盘有关的系统资源是插入符。但光标是全局共享资源,而插
入符是单线程共享资源,它被运行在同一线程上的所有窗口共享。
(1)创建、销毁插入符:插入符应在接收到输入焦点时创建,在失去输入焦点时销毁。可
通过函数 CWnd::CreateCaret()或 CWnd::CreateGrayCaret()创建插入符,通过函数::DestroyCaret()
销毁插入符。
(2)显示隐藏插入符:要使创建后的插入符可见,必须调用函数 CWnd::ShowCaret()显示
插入符,而调用 CWnd::HideCaret()函数则可以隐藏插入符。
(3)移动检索插入符:控制插入符的移动是开发者的工作,可以调用函数 CWnd::Set
CaretPos()移动插入符。而如果用户需要获取插入符的位置,可调用函数 CWnd::GetCaretPos()
检索插入符的位置信息。
说明:限于篇幅,各函数的原型不详细介绍,可参见MSDN帮助。
简单的插入符操作代码可表示如下:
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
CreateGrayCaret(5, 10); //创建插入符
SetCaretPos (point); //将插入符移到鼠标点
ShowCaret (); //显示插入符
CView::OnLButtonDown(nFlags, point);
}
4.2.5 键盘处理编程实例
为了使用户熟练掌握键盘消息及其处理过程,下面介绍一个简单的键盘消息处理实例。
1.实例说明
本实例通过工程向导创建一个单文档工程,在其中响应键盘的 WM_KEYDOWN、WM_
第 4 章 基本输入处理
93
KEYUP 和 WM_CHAR 消息,实现下面的功能:即当用户按下了 Shift 键时,在视图窗口中显
示提示信息“用户按下了 Shift 键!”;当用户释放了 Shift 键时,在视图窗口中显示提示信息“用
户释放了 Shift 键!”;而当用户按下了 Shift 键后又按下了字符“B”键,在视图窗口中显示提示
信息“用户同时按下 Shift 键和 B 键!”,结果如图 4.7 所示。
图 4.7 程序运行结果
2.开发过程
该实例的实现过程可表示如下。
(1)创建工程。启动 Visual C++,利用 MFC AppWizard[EXE]建立一个新的 MFC 工程,工
程名为“KeyboardDemo”,在 MFC AppWizard Step 1 的时候选择“Single documents”即基于单
文档的 MFC 工程,之后的步骤使用默认值。
(2)添加键盘消息及处理函数。在工程中,使用“Ctrl”+“W”快捷键(也可执行菜单命
令“View”→“ClassWizard”),弹出“MFC ClassWizard”对话框,如图 4.8 所示。在“ClassName”
列表框中,选择键盘消息的处理类“CKeyboardDemoView”,在“Object IDs”列表框中选择
图 4.8 “MFC ClassWizard”对话框
Visual C++ 6.0 开发指南
94
“CKeyboardDemoView”,则在“Message”列表框中,列出了 MFC 为其预定义的消息,分别选
择 WM_KEYDOWN、WM_KEYUP 和 WM_CHAR 消息,单击“Add Function”按钮,MFC 就
会为其自动添加相应的消息映射宏和消息处理函数。
此时在资源文件“KeyboardDemoView.cpp”中自动生成了键盘消息的消息映射宏,如下:
BEGIN_MESSAGE_MAP(CKeyboardDemoView, CView)
//{{AFX_MSG_MAP(CKeyboardDemoView)
ON_WM_KEYDOWN()
ON_WM_KEYUP()
ON_WM_CHAR()
//}}AFX_MSG_MAP
// Standard printing commands
……
END_MESSAGE_MAP()
并分别为其添加了消息映射函数,如下:
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
(3)添加实现代码:在资源文件“KeyboardDemoView.cpp”中添加各键盘消息函数的实现
代码。
void CKeyboardDemoView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(nChar==VK_SHIFT) //判断 Shift键是否被按下
{
//AfxMessageBox("dd");
bShiftdown=TRUE;
bShiftup=FALSE;
Invalidate(TRUE); //显示信息
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CKeyboardDemoView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(nChar==VK_SHIFT) //判断 Shift键是否被释放
{
//AfxMessageBox("dd");
bShiftup=TRUE;
Invalidate(TRUE); //显示信息
bShiftdown=FALSE;
}
第 4 章 基本输入处理
95
CView::OnKeyUp(nChar, nRepCnt, nFlags);
}
void CKeyboardDemoView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if((nChar==98)||(nChar==66)) //判断是否敲击了字符键 B键或 b键
{
if(bShiftdown)
{
bShiftB=TRUE;
bShiftdown=FALSE;
Invalidate(TRUE); //显示信息
}
}
CView::OnChar(nChar, nRepCnt, nFlags);
}
其中指示变量 bShiftdown、bShiftup、 bShiftB 均为 BOOL 型,在头文件“KeyboardDemo
View.h”中声明,在资源文件“KeyboardDemoView.cpp”的构造函数中赋初值“False”。
CKeyboardDemoView::CKeyboardDemoView()
{
// TODO: add construction code here
bShiftdown=bShiftup=bShiftB=FALSE;//赋初值
}
在资源文件“KeyboardDemoView.cpp”的 OnDraw 函数中,实现在客户区窗口输出按键提
示信息。
void CKeyboardDemoView::OnDraw(CDC* pDC)
{
CKeyboardDemoDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
if(bShiftdown) //按下了 Shift键
{
pDC->TextOut(20,20,"用户按下了 Shift键!");
}
if(bShiftup) //释放了 Shift键
{
pDC->TextOut(20,20,"用户释放了 Shift键!");
}
if(bShiftB) //同时按下了 Shift键和 B键
{
pDC->TextOut(20,20,"用户同时按下 Shift键和 B键!");
bShiftB=FALSE;
}
}
Visual C++ 6.0 开发指南
96
(4)编译运行:利用快捷键“F5”编译运行工程。当用户按下、释放 Shift 键,同时按下
Shift 键和 B 键时,客户窗口就显示相应的提示信息。
3.程序要点
(1)当用户按下了键盘中的某一个键时,首先产生 WM_KEYDOWN 消息,进入其消息处
理函数 OnKeyDown。该函数采用语句 if(nChar==VK_SHIFT)判断该键是否为 Shift 键。此函数
参数 nChar 为虚键码,如表 4-8 所示。此语句的 VK_SHIFT 也可以用其实际值 16 代替。
(2)如果该键为字符键,之后还将产生 WM_CHAR 消息,进入消息处理函数 OnChar。该
函数采用 if((nChar==98)||(nChar==66))语句判断该键是否为字符“B”或“b”键。OnChar 函数
的参数 nChar 并不是虚键码,而是由 TranslateMessage 函数根据虚键码编译而成的 Windows 字
符集的字符代码,默认的为 ASCII 码。常见的字符及其 ASCII 码值如表 4-10 所示。
表 4-10 常见的字符及其 ASCII码值
字 符 符 号 ASCII 码值 字 符 符 号 ASCII 码值 字 符 符 号 ASCII 码值
! 33 “ 34 # 35
$ 36 % 37 & 38
‘ 39 ( 40 ) 41
* 42 + 43 , 44
- 45 . 46 / 47
0~9 48~57 : 58 ; 59
< 60 = 61 > 62
? 63 @ 64 A~Z 65~90
[ 91 \ 92 ] 93
^ 94 _ 95 ` 96
a~z 97~122 { 123 | 124
} 125 ~ 126
可见,98、66 分别代表字符“b”和字符“B”。
(3)当释放按键时,产生 WM_KEYUP 消息,进入消息处理函数 OnChar,与函数 OnKeyDown
处理过程基本相同。
说明:有关刷新窗口函数 Invalidate以及 OnDraw函数显示提示信息,可参见本书第 6章的
介绍。
4.3 鼠标消息及其处理
鼠标是 Windows 最主要的输入设备。将鼠标的硬件接入系统,需要驱动程序。驱动程序将
把鼠标的硬件底层信号编译成 Windows 可以识别的信号。Windows 将根据这些信息构造鼠标消
息,并把它发送到应用程序的消息队列中。
第 4 章 基本输入处理
97
鼠标消息分为两类:客户区鼠标消息和非客户区鼠标消息。客户区是指窗口内的区域,一
般应用程序在这个区域显示 Windows 操作结果。非客户区则为窗口的其他组成部分,如系统菜
单、标题栏、水平、垂直滚动条、最大、最小化按钮、应用程序菜单以及边框等。
4.3.1 客户区鼠标消息
在客户区操作鼠标将产生客户区鼠标消息。鼠标的操作主要有 3 种基本方式,即单击、双
击和移动。单击动作则分为键按下操作和键释放操作。操作的键又可分为左键、右键和中键。
另外,还包括鼠标滚轮的消息。因此,客户区鼠标消息有十余种,常用的消息及 MFC 消息映
射宏如表 4-11 所示。
说明:鼠标可以和〈Shift〉或〈Ctrl〉键组合使用。
表 4-11 客户区鼠标消息
客户区鼠标消息 消息映射宏 描 述
WM_LBUTTONDOWN ON_WM_LBUTTONDOWN 在客户区内按下鼠标左键
WM_LBUTTONUP ON_WM_LBUTTONUP 在客户区内松开鼠标左键
WM_LBUTTONDBLCLK ON_WM_LBUTTONDBLCLK 在客户区内双击鼠标左键
WM_MBUTTONDOWN ON_WM_MBUTTONDOWN 在客户区内按下鼠标中键
WM_MBUTTONUP ON_WM_MBUTTONUP 在客户区内松开鼠标中键
WM_MBUTTONDBLCLK ON_WM_MBUTTONDBLCLK 在客户区内双击鼠标中键
WM_RBUTTONDOWN ON_WM_RBUTTONDOWN 在客户区内按下鼠标右键
WM_RBUTTONUP ON_WM_RBUTTONUP 在客户区内松开鼠标右键
WM_RBUTTONDBLCLK ON_WM_RBUTTONDBLCLK 在客户区内双击鼠标右键
WM_MOUSEACTIVE ON_WM_MOUSEACTIVE 在客户区内改变鼠标的激活状态
WM_MOUSEMOVE ON_WM_MOUSEMOVE 鼠标在客户区移动
WM_MOUSEWHEEL ON_WM_MOUSEWHEEL 在客户区内鼠标滚轮滚动
这些消息映射宏对应的消息处理函数就是在消息名前去除“WM_”前缀,换成“on”前缀。
如对消息 WM_LBUTTONDOWN,消息映射宏的处理函数为 OnLButtonDown,其声明如下:
afx_msg void OnLButtonDown(UINT nFlags,CPoint point );
该函数实际上是 CWnd 类的函数。函数的两个参数是包装过的 Windows 消息结构 MSG 的
附加信息,nFlag 包装了 wParam 参数,为鼠标动作的条件标志,可能取值及其含义如下。
□ MK_LBUTTON:按下了鼠标的左键,等同于 WM_LBUTTON。
□ MK_MBUTTON:按下了鼠标的中键,等同于 WM_MBUTTON。
□ MK_RBUTTON:按下了鼠标的右键,等同于 WM_MBUTTON。
□ MK_CONTROL:按下了键盘上的 Ctrl 键。
□ MK_SHIFT:按下了键盘上的 Shift 键。
参数 point 为 CPoint 类对象,包装了 MSG 消息结构的 lParam 参数,记录当前光标的 x、y
Visual C++ 6.0 开发指南
98
坐标,这个位置坐标是以相对于窗口客户区左上角的设备坐标而言的。如有必要,可用
CDC::DPtoLP( )将其转换为逻辑坐标。
在实际编程中,常使用 nFlags 参数,指出消息生成时的鼠标键以及 Shift 和 Ctrl 的状态。
如检测 Shift 键和 Ctrl 键的状态,如下:
void OnLButtonDown(UINT nFlags,CPoint point )
{
if((nFlags &MK_CONTROL)&&( nFlags &MK_SHIFT)) //SHIFT键和 CTRL都被按下
…
}
通过 point 参数可以将鼠标操作与屏幕显示对应起来,例如在鼠标操作的绘图程序中,point
参数返回当前光标的位置,就可以根据 point 的值进行相应的绘图操作。
4.3.2 非客户区鼠标消息
当鼠标移动到非客户区时,将发出非客户区消息。非客户区中鼠标的动作和客户区一样,
因此其消息与客户区消息数量一样。常用的非客户区消息及 MFC 消息映射宏如表 4-12 所示。
表 4-12 非客户区鼠标消息
非客户区鼠标消息 消息映射宏 描 述
WM_NCLBUTTONDOWN ON_WM_NCLBUTTONDOWN 在非客户区内按下鼠标左键
WM_NCLBUTTONUP ON_WM_NCLBUTTONUP 在非客户区内松开鼠标左键
WM_NCLBUTTONDBLCLK ON_WM_NCLBUTTONDBLCLK 在非客户区内双击鼠标左键
WM_NCMBUTTONDOWN ON_WM_NCMBUTTONDOWN 在非客户区内按下鼠标中键
WM_NCMBUTTONUP ON_WM_NCMBUTTONUP 在非客户区内松开鼠标中键
WM_NCMBUTTONDBLCLK ON_WM_NCMBUTTONDBLCLK 在非客户区内双击鼠标中键
WM_NCRBUTTONDOWN ON_WM_NCRBUTTONDOWN 在非客户区内按下鼠标右键
WM_NCRBUTTONUP ON_WM_NCRBUTTONUP 在非客户区内松开鼠标右键
WM_NCRBUTTONDBLCLK ON_WM_NCRBUTTONDBLCLK 在非客户区内双击鼠标右键
WM_MOUSEMOVE ON_WM_NCMOUSEMOVE 鼠标在非客户区移动
WM_NCHITTEST ON_WM_NCHITTEST 非客户区鼠标消息检测
与客户区鼠标消息映射宏相似,这些消息映射宏对应的消息处理函数就是在消息名前去除
“WM_”前缀,换成“on”前缀。如对消息 WM_NCLBUTTONDOWN,消息映射宏的处理函
数为 OnNcLButtonDown,其声明如下。
afx_msg void OnNcLButtonDown(UINT nHitTest,CPoint point );
其中参数 point的含义与客户区鼠标消息映射函数一样,为鼠标光标的位置。而参数 nHitTest
则封装了消息结构 MSG 中的 WParam 参数,为非客户区标志,指明鼠标所处的位置属性。其
可能取值及其含义如下。
第 4 章 基本输入处理
99
□ HTBORDER:光标位于无缩放框的窗口边框上。
□ HTBOTTOM:光标位于窗口底部边框上。
□ HTBOTTOMLEFT:光标位于窗口左下角边框上。
□ HTBOTTOMRIGHT:光标位于窗口右下角边框上。
□ HTCAPTION:光标位于标题栏内。
□ HTERROR:光标位于窗口背景或窗口间边界(窗口无效范围)上。
□ HTGROWBOX:光标位于缩放窗。
□ HTHSCROLL:光标位于水平滚动条内。
□ HTLEFT:光标位于左边框上。
□ HTMAXBUTTON:光标位于最大化按钮上。
□ HTMENU:光标位于菜单区。
□ HTMINBUTTON:光标位于最小化按钮上。
□ HTRIGHT:光标位于右边框上。
□ HTSYSMENU:光标位于系统菜单上。
□ HTTOP:光标位于上边框上。
□ HTTOPLEFT:光标位于左上角边框上。
□ HTTOPRIGHT:光标位于右上角边框上。
□ HTTRANSPARENT:光标位于另一个窗口覆盖的区域。
□ HTVSCROLL:光标在垂直滚动条上。
说明:在一般情况下,用户并不需要直接编写非客户区鼠标消息的处理,因为这些处理一
般可以由Win32 API函数 DefWindowProc完成。该函数可以完成改变窗口的大小,对系统菜单、
最大化、最小化按钮控制等最常用的窗口功能。且在MFC中,MFC会自动完成这些Windows
操作。
在非客户区鼠标消息中,WM_NCHITTEST 消息有时可以用于检测非客户区鼠标消息发出
的位置属性。窗口在接受一个客户区或非客户区鼠标消息之前,首先接收到光标的屏幕坐标和
WM_NCHITTEST 消息,Windows 一般默认处理它。
消息映射宏 ON_WM_NCHITTEST 映射的处理函数为 OnNcHitTest,其声明如下。
afx_msg UINT OnNcHitTest(CPoint point );
该函数的作用就像“间谍”一样监视非客户区消息发出的位置属性。该函数返回 nHitTest
值,根据这个值可以判断鼠标所在的位置,如标题栏、滚动条、最大化按钮等。
注意:在 MFC 预定义的鼠标消息有 WM_LBUTTONDOWN、WM_LBUTTONUP、
WM_LBUTTONDBLCLK等 9个,也就是说只有这些鼠标消息才能使用 ClassWizard自动添加
消息映射宏及消息处理函数,对于那些没有预定义的鼠标消息的使用,需要用户手动添加消息
映射宏及消息处理函数。
4.3.3 鼠标捕捉
通常情况下,只有鼠标光标位于某一个窗口的客户区或非客户区时,该窗口的窗口函
Visual C++ 6.0 开发指南
100
数才能接收鼠标消息。如最常见的使用鼠标的画线程序,一般做法就是当在客户区选定一
个点,作为起点,按下鼠标左键,OnLButtonDown 函数中将记录此时坐标信息。而后移动
鼠标,直到移动