07界面编程5鼠标键盘与光标.docx
《07界面编程5鼠标键盘与光标.docx》由会员分享,可在线阅读,更多相关《07界面编程5鼠标键盘与光标.docx(18页珍藏版)》请在冰豆网上搜索。
07界面编程5鼠标键盘与光标
第7章界面编程5-鼠标、键盘与光标
Windows是一种基于消息的交互式GUI操作系统,用户的操作主要通过鼠标和键盘进行。
Windows利用消息来处理由用户操作所引发的鼠标和键盘事件,程序员一般通过在视图类中添加相应的消息响应函数(也叫事件处理程序),并编写具体响应代码来处理鼠标和键盘消息。
鼠标的当前位置由屏幕上的光标来表示,程序员可以根据需要来动态设置不同形状的光标。
本章先介绍鼠标与键盘消息及其响应,然后再讨论设置与隐藏光标的方法,最后给出一个综合利用鼠标与键盘消息响应和光标设置的实例。
7.1鼠标
鼠标(mouse)是GUI中最常用的指示跟踪设备,由美国斯坦福研究所的DouglasEngelbart和BillEnglish于1963年发明,BillEnglish于1972年发明滚球式(ball)纯机械鼠标,1983年罗技(Logitech)公司发明了第一只光学机械式(简称为光机式或机械式)鼠标,1999年微软与安捷伦(Aeilent,后改组为安华高,Avago)公司合作推出了IntelliEye光学引擎和世界上第一款不需专业鼠标垫的光电鼠标,2003年微软和罗技分别推出蓝牙无线鼠标,2005年罗技与安华高合作推出了第一款(无线)激光鼠标,2008年微软推出了采用BlueTrack技术几乎可兼容所有表面的蓝光鼠标。
现代鼠标一般为带滚轮的三键光电或激光式有线或无线(蓝牙或红外)鼠标。
7.1.1鼠标事件与消息
鼠标事件(mouseevent)指用户的鼠标操作,基本的鼠标操作有:
按下(press)和松开(release)鼠标键、单击(click)或双击(double-click)鼠标键、移动(move)或拖动(drag)鼠标,其中左鼠标键按下/松开、移动(拖动)鼠标在编程中最常用。
大多数鼠标操作都有相对应的Windows消息(参见表7-1,其中粗体部分为常用的)。
按照鼠标事件发生时鼠标光标所处的窗口区域,可以把鼠标消息分成两类:
●客户区(clientarea)鼠标消息:
鼠标光标位于窗口的客户区时所产生的鼠标消息。
●非客户区(nonclientarea)鼠标消息:
鼠标光标位于窗口的非客户区(如边框、标题条、系统菜单图标、最大化/最小化/还原按钮、关闭按钮、菜单、工具条、滚动条、状态条)时所产生的鼠标消息。
表7-1鼠标事件与消息
事件
客户区鼠标消息
非客户区鼠标消息
双击左键
WM_LBUTTONDBLCLK
WM_NCLBUTTONDBLCLK
按下左键
WM_LBUTTONDOWN
WM_NCLBUTTONDOWN
释放左键
WM_LBUTTONUP
WM_NCLBUTTONUP
双击中键
WM_MBUTTONDBLCLK
WM_NCMBUTTONDBLCLK
按下中键
WM_MBUTTONDOWN
WM_NCMBUTTONDOWN
释放中键
WM_MBUTTONUP
WM_NCMBUTTONUP
鼠标移动
WM_MOUSEMOVE
WM_NCMOUSEMOVE
双击右键
WM_RBUTTONDBLCLK
WM_NCRBUTTONDBLCLK
按下右键
WM_RBUTTONDOWN
WM_NCRBUTTONDOWN
释放右键
WM_RBUTTONUP
WM_NCRBUTTONUP
其中,只有客户区鼠标消息是常用的。
至于非客户区鼠标消息,除了Windows自己用于系统管理外一般很少使用。
在本课程中我们只讨论客户区鼠标消息及其处理。
在Windows的SDK编程中,必须将非客户区鼠标消息交给DefWindowProc函数处理。
因为有的鼠标没有中键,所以应用程序很少使用与中键有关的三个消息。
另外,双击的最大时间间隔(默认为500毫秒)可用下列函数来获取或设置:
UINTGetDoubleClickTime(VOID);//返回间隔的毫秒数
BOOLSetDoubleClickTime(UINTuInterval);//若成功返回非0值
其中,uInterval为时间间隔值(毫秒数)。
7.1.2响应鼠标消息
可以用MFC的类向导,为视图类或对话框类等CCmdTarget类的派生类,添加鼠标消息响应函数。
常用的鼠标消息响应函数的原型为:
afx_msgvoidOnMouseMove(UINTnFlags,CPointpoint);
afx_msgvoidOnLButtonDown(UINTnFlags,CPointpoint);
afx_msgvoidOnLButtonUp(UINTnFlags,CPointpoint);
afx_msgvoidOnLButtonDblClk(UINTnFlags,CPointpoint);
afx_msgvoidOnRButtonUp(UINTnFlags,CPointpoint);
其中的参数:
point为鼠标的位置坐标(相对于客户区的左上角)、nFlags为标志参数——可取表7-2中所列的符号常量值(二进制位,可以用“与”来判断,用“或”来组合)。
表7-2鼠标响应函数中标志参数nFlags的取值
符号常量
数值
含义
MK_CONTROL
8
Ctrl键被按下
MK_LBUTTON
1
左鼠标键被按下
MK_MBUTTON
16
中鼠标键被按下
MK_RBUTTON
2
右鼠标键被按下
MK_SHIFT
4
Shift键被按下
与菜单项的事件处理程序类似,每次当你添加一个鼠标消息(如WM_MOUSEMOVE)响应时,MFC类向导会做如下一系列工作:
●在头文件(*.h)的类定义中,添加消息响应函数的原型。
如:
afx_msgvoidOnMouseMove(UINTnFlags,CPointpoint);
●在代码文件(*.cpp)尾部,添加含默认代码的消息响应函数体。
如:
voidCStudentView:
:
OnMouseMove(UINTnFlags,CPointpoint)
{
//TODO:
在此添加消息处理程序代码和/或调用默认值
CView:
:
OnMouseMove(nFlags,point);
}
●在代码文件(*.cpp)头部,添加对应的消息映射宏。
如:
BEGIN_MESSAGE_MAP(CStudentView,CView)
……
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
与菜单的事件处理程序相比,鼠标的消息响应函数有了输入参数,而消息映射宏没有输入参数,且消息映射宏的名称是在窗口消息的对应符号常量前添加“ON_”即可。
7.1.3单击和拖动
注意,在Windows的鼠标消息中,并没有单击和拖动消息。
编程中,单击操作一般用松开左鼠标键消息WM_LBUTTONUP来代替,如:
voidCStudentView:
:
OnLButtonUp(UINTnFlags,CPointpoint){
......//处理鼠标单击消息的代码
CView:
:
OnLButtonUp(nFlags,point);
}
而拖动操作则在鼠标移动的消息响应中判断左鼠标键是否被按下来区分。
如:
voidCStudentView:
:
OnMouseMove(UINTnFlags,CPointpoint){
if(nFlags&MK_LBUTTON){
......//处理鼠标拖动消息的代码
}
CView:
:
OnMouseMove(nFlags,point);
}
7.1.4捕捉鼠标
因为鼠标消息只发给位于当前光标之下的窗口,所以如果用户在客户区按下鼠标,拖动鼠标到客户区外后才释放鼠标,则应用程序得不到鼠标被释放的消息,可能会产生错误的响应操作。
为避免这种事情发生,可以调用从CWnd类继承的成员函数SetCapture来捕捉鼠标,使得以后的鼠标消息都发给自己,而不论鼠标的光标是否在自己窗口的客户区。
当程序不再需要鼠标消息后,必须调用API中的全局函数ReleaseCapture来释放对鼠标的捕捉,从而使其他程序可以获得它自己的鼠标消息。
在窗口失去对鼠标的捕捉时,会收到Windows发送的WM_CAPTURECHANGED消息。
还可以调用从CWnd类继承来的成员函数GetCapture来获得当前捕捉鼠标的窗口的指针。
这些函数的原型如下:
CWnd*SetCapture();
BOOLReleaseCapture();
staticCWnd*PASCALGetCapture();
Windows应用程序一般是在用户按下鼠标键时设置鼠标捕捉,而在用户释放鼠标键后释放鼠标捕捉。
如:
voidCDrawView:
:
OnLButtonDown(UINTnFlags,CPointpoint){
m_bLButtonDown=TRUE;
SetCapture();
......
CView:
:
OnLButtonDown(nFlags,point);
}
voidCDrawView:
:
OnLButtonUp(UINTnFlags,CPointpoint){
......
m_bLButtonDown=FALSE;
ReleaseCapture();
CView:
:
OnLButtonUp(nFlags,point);
}
7.2键盘
与鼠标一样,当用户敲击键盘时,会产生键盘事件(keyboardevent)。
在MFC中,它们也是由消息、消息映射宏和消息响应函数来处理。
7.2.1按键
Windows中的按键有两类:
●普通按键:
在拥有键盘输入焦点的窗口中,在没有按下Alt键时情况下,所按的各种(组合)键。
●系统按键:
按下Alt键时的各种按键;或在没有任何拥有键盘输入焦点的窗口时,所按下的任意(组合)键。
为了保证像Alt+F4(关闭窗口、程序或操作系统)这样的系统命令能被正确的执行,Windows总是将系统按键所产生的各种键盘消息,发送给CWnd类的对应系统消息响应函数作默认处理。
7.2.2输入焦点
由于Windows为多用户多任务的操作系统,正在运行的程序可能有多个。
对于鼠标,Windows可根据其当前光标位置之下的窗口是哪一个,来决定将鼠标消息发送给哪个窗口(设置了鼠标捕捉时除外)。
而对于键盘,只有拥有键盘输入焦点的窗口才能接收到Windows所发送的键盘消息。
整个操作系统,在任一时刻,最多只能有一个输入焦点。
通常情况下,拥有键盘输入焦点的窗口是当前被激活的窗口。
活动窗口(包括对话框、各种控件和图标)会被高亮显示。
窗口在获得或失去输入焦点时,会收到Windows发送的WM_SETFOCUS或WM_KILLFOCUS消息。
可在相应的响应函数OnSetFocus或OnKillFocus中加入自己的处理代码。
如:
voidCStudentView:
:
OnSetFocus(CWnd*pOldWnd)
{
CView:
:
OnSetFocus(pOldWnd);
//TODO:
在此处添加消息处理程序代码
……
}
也可以自己调用(全局)API函数SetFocus和GetFocus来设置和获得输入焦点:
HWNDSetFocus(HWNDhWnd);//hWnd为将获得输入焦点的窗口句柄
//返回失去输入焦点的窗口句柄
HWNDGetFocus(VOID);//返回拥有输入焦点的窗口句柄
7.2.3键盘消息
Windows中的键盘消息有两类:
●击键消息(keystrokemessage):
由键盘动作直接产生的消息,如键按下或释放。
⏹普通击键消息:
WM_KEYDOWN、WM_KEYUP。
⏹系统击键消息:
WM_SYSKEYDOWN、WM_SYSKEYUP。
●字符消息(charactermessage):
由击键消息转化而产生的消息。
⏹普通字符消息:
WM_CHAR。
⏹系统字符消息:
WM_SYSCHAR。
其中。
常用的是WM_KEYDOWN和WM_CHAR。
7.2.4消息响应
普通的击键和字符消息WM_KEYDOWN、WM_KEYUP和WM_CHAR所对应的消息响应函数的原型为:
afx_msgvoidOnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags);
afx_msgvoidOnKeyUp(UINTnChar,UINTnRepCnt,UINTnFlags);
afx_msgvoidOnChar(UINTnChar,UINTnRepCnt,UINTnFlags);
它们的输入参数相同:
●nChar:
⏹对OnKeyDown和OnKeyUp为不区分大小写的虚拟键码(virtual-keycode),如’0’、’A’、VK_F1、VK_LEFT等。
⏹对OnChar为字符的ASCII码(charactercode)。
●nFlags:
标志。
⏹对OnChar,一般不用。
⏹对OnKeyDown和OnKeyUp,nFlags中各位的意义见表7-3。
表7-3键盘消息响应的标志参数nFlags中各位的含意
位
意义
0~7
8位扫描码(值依赖于OEM)
8
为扩展键*时为1否则为0
9~10
没有使用
11~12
由Windows在内部使用
13
Alt键被按下时为1否则为0
14
键原来的状态(按下为1,松开为0)
*扩展键:
指非基本键盘中的键,如Insert/Delete/Home/End/PageUp/PageDown、方向键、Windows键/应用键和数字键盘中的NumLock、/、Enter键等。
例如:
if(!
(nFlags&1<<14))……//如果键原来是松开的
●nRepCnt:
为按住键不放时,所产生的重复按键计数值,一般为1。
可以调用API函数GetKeyState来获得指定虚键的状态,其函数原型为:
SHORTGetKeyState(intnVirtKey);
返回值类型SHORT是short的typedef类型,当指定的键被按下时,返回值的高位为1;当指定的键被锁住时,返回值的低位为1。
例如
if(GetKeyState(VK_SHIFT)&1<<15)...;//Shift键被按下
if(GetKeyState(VK_CAPITAL)&1)...;//CapsLock键被锁住
7.2.5虚拟键表
Windows的虚拟键代码(virtual-keycode)见表7-4。
表7-4虚拟键代码表
键类别
键
代码值
键类别
键
代码值
字母
A
‘A’
控制
LeftWindows
VK_LWIN
┊
┊
NumLock
VK_NUMLOCK
Z
’Z’
PageDown
VK_NEXT
数字
(主键盘)
0)
‘0’
PageUp
VK_PRIOR
┊
┊
PrintScreen
SysRq
VK_SNAPSHOT
9(
’9’
RightWindows
VK_RWIN
数字
(数字键盘)
0Ins
VK_NUMPAD0
ScrollLock
VK_SCROLL
┊
┊
Shift
VK_SHIFT
9PgUp
VK_NUMPAD9
Tab
VK_TAB
功能
F1
VK_F1
符号
SpaceBar
VK_SPACE
┊
┊
+
VK_ADD
F12
VK_F12
-
VK_SUBTRACT
方向
↑
VK_UP
*
VK_MULTIPLY
↓
VK_DOWN
/
VK_DIVIDE
←
VK_LEFT
.Del
VK_DECIMAL
→
VK_RIGHT
;:
186/0xBA
控制
Alt
VK_MENU
=+
187/0xBB
Applications
VK_APPS
<
188/0xBC
Backspace
VK_BACK
-_
189/0xBD
CapsLock
VK_CAPITAL
.>
190/0xBE
Ctrl
VK_CONTROL
/?
191/0xBF
Delete
VK_DELETE
`~
192/0xC0
End
VK_END
[{
219/0xDB
Enter
VK_RETURN
\|
220/0xDC
Esc
VK_ESCAPE
]}
221/0xDD
Home
VK_HOME
‘“
222/0xDE
Insert
VK_INSERT
7.3光标
光标(cursor)是一种具有热点(hotspot)的特殊位图资源(一般为32*32或64*64像素),用于表示鼠标位置和操作状态。
Windows的默认光标为指向左上方的箭头。
光标文件的扩展名为cur,在Windows操作系统的光标子目录(如C:
\Windows\Cursors)中,含有许多常用的光标文件(*.cur)。
7.3.1装入与设置光标
应用程序有时需要设置和显示自己选择或创建的光标,这需要调用WindowsSDK的API(全局)函数SetCursor和LoadCursor,它们的函数原型为:
HCURSORSetCursor(//返回原来光标的句柄
HCURSORhCursor//光标的句柄
);
HCURSORLoadCursor(//返回新装入光标的句柄
HINSTANCEhInstance,//应用程序实例的句柄
LPCTSTRlpCursorName//名串或光标资源ID
);
其中,hInstance一般置为NULL,lpCursorName可取若干符号常量值(参见表7-5)。
表7-5lpCursorName的常用取值(预定义光标)
符号常量
光标
形状
操作
IDC_ARROW
标准箭头(Standardarrow)
指向
IDC_CROSS
十字标线(Crosshair)
绘图
IDC_IBEAM
I形条(I-beam)
文本
IDC_WAIT
沙漏(Hourglass)
等待
IDC_APPSTARTING
标准箭头与小沙漏
等待
IDC_HAND
手形(Win2K以上)
超链接
IDC_HELP
箭头与问号(mark)
相关帮助
IDC_NO
斜圆(Slashedcircle)
禁止
IDC_SIZEALL
四箭头
移动
IDC_SIZENESW
下斜双箭头
改变大小
IDC_SIZENS
垂直双箭头
改变高度
IDC_SIZENWSE
上斜双箭头
改变大小
IDC_SIZEWE
水平双箭头
改变宽度
IDC_UPARROW
上箭头
选择列
例如,装入并设置沙漏光标:
SetCursor(LoadCursor(NULL,IDC_WAIT));
在CWinApp类中也有两个LoadCursor函数,其原型为:
HCURSORLoadCursor(LPCTSTRlpszResourceName)const;
HCURSORLoadCursor(UINTnIDResource)const;
其中,返回值都为新装入光标的句柄,lpszResourceName与nIDResource分别为光标资源的名串与ID。
如
externCDrawApptheApp;
SetCursor(theApp.LoadCursor(IDC_MYCURSOR));
注意:
若类光标非空(默认为非空),则在设置光标后,每当用户移动鼠标时,Windows系统就会自动恢复类光标。
解决办法是在移动鼠标消息的响应函数OnMouseMove中来设置光标,如:
voidCDrawView:
:
OnMouseMove(UINTnFlags,CPointpoint){
SetCapture();
SetCursor(LoadCursor(NULL,IDC_CROSS));
......
CView:
:
OnMouseMove(nFlags,point);
}
也可在设置光标的同时也设置鼠标捕捉,则Windows会在释放鼠标捕捉后才恢复类光标。
7.3.2显示与隐藏光标
系统为每个应用程序维护一个显示与隐藏光标的内部显示计数(internaldisplaycounter)。
当此计数的值≥0时,显示鼠标光标。
当此计数的值=0时,隐藏鼠标光标。
程序有时(如截屏)需要先隐藏光标,在完成操作后再显示光标。
装入鼠标前,该计数的初始值为0。
可用API函数ShowCursor来增加/减少此计数(显示/隐藏光标),其函数原型为:
intShowCursor(BOOLbShow);//返回新的计数值
其中,bShow=TRUE/FALSE对应于计数+1/-1。
例如:
ShowCursor(FALSE);//隐藏光标
……//完成某项操作
ShowCursor(TRUE);//显示光标
7.3.3裁剪光标
可以用API函数ClipCursor将你的光标限制在由lpRect指定的矩形区域内:
BOOLClipCursor(CONSTRECT*lpRect);//成功返回非0
其中,矩形区域的坐标是相对于整个屏幕的,若lpRect为NULL,则取消对鼠标移动的限制。
例如:
CRectrect(100,100,300,300);//定义矩形对象
ClipCursor(&rect);//裁剪光标
……
ClipCursor(NULL);//取消裁剪
可以用API函数GetClipCursor来获得当前鼠标的裁剪区域,坐标也为屏幕的坐标。
若无裁剪区,则lpRect中的矩形为整个屏幕。
其函数原型为:
BOOLGetClipCursor(LPRECTlpRect);//成功返回非0
7.3.4获取与设置光标位置
除了可在鼠标消息的响应函数中,由输入参数point得到鼠标位置外,还可以在程序代码的任何地方调用API函数GetCursorPos来得到当前的鼠标位置,该函数的原型为:
BOOLGetCursorPos(LPPOINTlpPoint);//成功返回非0
其中,lpPoint的坐标为屏幕的坐标,而且与映射模式无关。
还可以调用另一个API函数SetCursorPos来设置鼠标位置(也为屏幕的坐标):
BOOLSetCursorPos(intX,intY);//成功返回非0
注意,调用此设置光标位置函数,会自动触发鼠标移动事件。
7.4例子
在以后绘图和其他程序中,会用到多种鼠标与键盘消息及其响应操作,有时也需要设置自己