7.1、键盘

键盘和鼠标是两个标准的用户输入源,在一些交叠的操作中通常相互补充使用。
1.1
输入方式:以消息的形式传递给程序的窗口处理过程。
Windows用8种不同的消息来传递不同的键盘事件。这好像有点多,但是程序可以忽略其中至少一半的消息而不会有任何问题。并且,在大多数情况下,这些消息中包含的键盘信息会多于程序所需要的。处理键盘的部分工作就是识别出哪些消息是重要的,哪些是不重要的。
 
1.2扫描码和虚键码
	系统通过安装当前键盘的设备驱动来实现与应用程序的设备无关性,也可以通过用户或应用程序的键盘布局设置来实现语言无关性。键盘设备驱动接收键盘的“扫描码”,然后把“扫描码”发送给键盘布局,通过键盘布局被转换为消息并发送到应用程序的相应窗口。
	扫描码:键盘上每个键都有一个唯一值,这个值就称为扫描码。“扫描码”是设备相关的,按键时会产生两次扫描码,一次按下和一次释放。
	虚拟码:键盘驱动把扫描码解映射为“虚键码”(virtual-key code),这个码是设备无关的,其值被系统所定义并用来标识每一个键。
	转换扫描码后,键盘布局会创建一个包含扫描码虚键码以及其他按键信息的消息,并把这个消息放入系统消息队列。接着,系统从系统消息队列中删除该消息,再投递到相应线程的消息队列中。最后,线程的消息循环移除该消息并传递到相应窗口过程以进行处理。下图即键盘输入模型:

虚键码描述
	按键消息的wParam参数包含了按键的虚键码,窗口处理过程根据这个虚键码来处理或者忽略一个按键消息。
	典型的窗口处理过程中仅会处理一小部分按键消息,其余的部分只是简单的接收并忽略。例如,窗口处理过程可能仅处理WM_KEYDOWN消息,以及光标移动键、换档键(也可以说控制键),还有功能键的虚键码。窗口处理过程中一般不会处理字符键的按键消息,相反,应该使用TranslateMessage函数把它们转换成字符消息。
 
 
1.3输入焦点
  同一时刻,Windows中可能有多个不同的程序在运行,也就是说有多个窗口同时存在。这时,键盘由多个窗口共享,但只有一个窗口能够接收到键盘消息,这个能够接收键盘消息的窗口被称为拥有输入焦点的窗口。
	拥有输入焦点的窗口是当前的活动窗口,或者是活动窗口的子窗口,其标题和边框会以高亮度显示,以区别于其他窗口。拥有输入焦点的也可以是图标而不是窗口,此时,Windows也将消息发送给图标,只是消息的格式略有不同。
线程可以通过GetFocus函数确定哪个窗口为当前窗口(获得焦点)。
	窗口过程可以通过发送WM_SETFOCUSWM_KILLFOCUS消息使窗体获得或失去输入焦点。程序也可以通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来判断窗体何时获得或失去输入焦点。
 
1.4按键消息
	按键会产生WM_KEYDOWN或WM_SYSKEYDOWN消息,然后会被放置在当前键盘焦点窗口所在线程的消息队列中。同样释放产生WM_KEYUP或者WM_SYSKEYUP消息。
	Key-up与Key-down消息通常应该成对出现,但如果用户按下键后呆足够长时间的话,键盘会自动重复描述这一情况,系统会对应产生一系列的WM_KEYDOWN或WM_SYSKEYDOWN事件,但不管怎样,用户释放按键时,只会产生一个WM_KEYUP或WM_SYSKEYUP消息。

系统及非系统按键
  系统中系统按键与非系统按键是不同的,系统按键产生系统按键消息:WM_SYSKEYDOWN、WM_SYSKEYUP,而非系统按键产生非系统按键消息:WM_KEYDOWN与WM_KEYUP。
  如果你的窗口处理过程确实有必要处理系统按键消息的话,一定要确认在处理完毕后,该过程把消息传递给了DefWindowProc函数。否则,所有的系统操作,包括ALT键都会失效,即便窗口的确获得了焦点。也就是说,用户将不能访问窗口菜单或者系统菜单,又或者使用ALT+ESC(ALT+TAB)组合键激活其他窗口了。

  系统按键消息主要是系统使用的,系统用这些消息提供菜单的内置键盘接口,以及允许用户控制激活不同的窗口。系统按键消息通常是用户按下ALT及某个键的组合键时产生的,又或者在用户按下但没有窗体拥有键盘焦点(比如,激活的应用程序最小化)时产生。如果消息产生的话,就会发送到激活窗体的消息队列中。
	非系统按键消息是需要应用程序窗体处理的,DefWindowProc函数不会对这些消息作任何处理,窗体的处理过程可以忽略任意的不需要的非系统按键消息。
 
1.6按键消息标志
 按键消息的lParam消息中包含了按键的额外信息,其中包括:重复次数、扫描码、扩充键标志、上下文标志、前键状态标志,以及转换状态标志。如图:
 
按键标志中可以存储以下值:
KF_ALTDOWN     ALT键标志,标识ALT键是否按下。
KF_DLGMODE     对话框标志,标识对话框是否激活。
KF_EXTENDED   扩充键标志
KF_MENUMODE 菜单模式标志,标识菜单是否激活。
KF_REPEAT  重复次数
KF_UP   转换状态标志

重复次数
  你可以通过检查重复次数,来确定一次按键是否产生了多个按键消息。如果键盘产生WM_KEYDOWN或WM_SYSKEYDOWN消息后,超过一定时间应用程序还未处理这些消息的话,系统就会增加重复计数。通常,是因为用户保持按键状态较长时间,而启动了键盘的自动重复技术机制。系统不会因此产生多个键盘消息,相反,系统会组合这些消息,并增加这个消息的重复次数。释放一个按键时不会启动自动重复机制,所以WM_KEYUP与WM_SYSKEYUP消息的重复次数总会是1。

扫描码
  扫描码是用户按键时由键盘硬件产生的,这个值是设备相关的,用来标识不同的键,对于字符也是通过按键来表示的。应用程序通常会忽略扫描码,使用设备无关的虚键码来说明按键消息。

扩充键标志
  扩充键标志用来标识按键消息中是否包含了增强型键盘的附加键,这些扩充键包括:键盘右手边的ALT、CTRL键,INS、DEL、HOME、END、PAGE UP、PAGE DOWN,小键盘左边的方向键,NUM LOCK、BREAK(CTRL+PAUSE)、PRINT SCRNT以及小键盘上的除号(/)键及ENTER键。如果键为以上键的话,扩充键标志即会设置。

上下文标志
  上下文标志是为了说明按键消息产生时,ALT键是否已经按下,如果为1,表示ALT键已经按下,否则没有按下。

前键状态标志
  前键状态标志用来说明产生按键消息的键原来是抬起的还是按下的。如果为1,表示原来是按下的,0原来是抬起的。你可以通过该标志来辨别该消息是否是由键盘自动重复机制产生的。如果为1,表示WM_KEYDOWN与WM_SYSKEYDOWN消息是自动产生的,对于WM_KEYUP与WM_SYSKEYUP消息来说,该标志总会为0。

转换状态标志
  转换状态标志用来说明该消息是按下键时还是释放键时产生的,对于WM_KEYDOWN、WM_SYSKEYDOWN来说该标志总会为0,对于WM_KEYUP、WM_SYSKEYUP总会是1。

应用实例:
本程序实现在客户区画矩形或椭圆,按C键显示椭圆,按R键显示矩形;当按“←”、“↑”、“→”、“↓”键时,屏幕上的图形相应移动。
1、定义变量
CRect m_rect;
m_isCircle;

2、变量初始化
m_rect=CRect(10,10,100,100);   //圆或矩形的初始位置
m_isCircle=TRUE;   //初始画园

3、添加字符消息的映射函数
<pre name="code" class="cpp"><p style="font-family: Arial; font-size: 14px; line-height: 26px;"><pre name="code" class="cpp">void CKeyMsgView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
//TODO: Add your message handler code here and/or call default
bool nOldShape=m_IsCircle;
//保存旧的图形形状,上一次图形代码和这一次的图形代码不一样时才重新绘图
switch (nChar)
{
case 'c':
case 'C':
m_IsCircle=true; //圆的代码
break;
case 'r':
case 'R':
m_IsCircle=false; //矩形的代码
break;
}
if(nOldShape!=m_IsCircle)
InvalidateRect(m_Rect);
/*若当前按的键与前一次的不同,则调用InvalidateRect(),该函数使得屏幕的指定矩形区域无效,它发送WM_PAINT消息,从而间接地调用OnDraw(CDC*pDC)函数,使得整个屏幕重新绘制一遍*/
CView::OnChar(nChar, nRepCnt, nFlags);
}
(5)添加击键消息的映射函数

与上一步类似,用ClassWizard为视图类CKeyMsgView添加击键消息WM_KEYDOWN的消息映射函数OnKeyDown(),并在该函数中添加如下代码,用于移动屏幕上的图形。

void CKeyMsgView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
//TODO: Add your message handler code here and/or call default
CPoint ptOffSet=CPoint(0, 0);//偏移点为(0, 0)
switch(nChar)
{
case VK_LEFT:
ptOffSet=CPoint(-2,0);//左移两个像素
break;
case VK_RIGHT:
ptOffSet=CPoint(2, 0); //右移
break;
case VK_UP:
ptOffSet=CPoint(0,-2);//上移
break;
case VK_DOWN:
ptOffSet=CPoint(0, 2);//下移
break;
}
m_Rect.OffsetRect(ptOffSet); //使得矩形偏移
Invalidate(); //刷新屏幕
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

(6)重载视图类的虚函数OnDraw(),实现屏幕图形的绘制

void CKeyMsgView::OnDraw(CDC* pDC)
{
CKeyMsgDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if(m_IsCircle) //如果画圆
pDC->Ellipse(m_Rect); //画圆
else//如果不是画圆的话
pDC->Rectangle(m_Rect);//画矩形
}




总结整理自网络



                
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值