处理键盘和鼠标响应消息

搜狐博客 > 碧荷竹轩 > 日志 > vc .NET程序设计 2007-04-26 | PreTranslateMessage()函数说明 1.函数原型:(MSDN)

virtual BOOL PreTranslateMessage( MSG *pMsg );

功能:

    Override   this   function   to   filter   window   messages   before   they   are   dispatched   to   the   Windows   functions TranslateMessage   and DispatchMessage. The   default   implementation   performs   accelerator-key   translation,   so   you   must   call   the   CWinApp::PreTranslateMessage   member   function   in   your   overridden   version.   
  重载该函数可以实现窗口消息在派发给窗口函数TrnaslateMessage和DispatchMessae()之前的过滤.缺省的实现是完成加速键的翻译.因为您必须在你的重载版本中调用CWinApp:PreTranslateMessage()函数.

在MFC中,PreTranslateMessage是虚函数,我们可以重载它来处理键盘和鼠标消息。   
在sdk中,这又有所不同,我们必须在回调函数中LRESULT   CALLBACK   WndProc(HWND   hWnd,   UINT   message,   WPARAM   wParam,   LPARAM   lParam)处理消息:   
  它和PreTranslateMessage起的作用是类似的。只是MFC封装的更好而已

2.说明:

该函数表示在消息处理(TranslateMessge()和DispatchMessage()等)前所作的操作,如果函数返回值为TRUE,那么消息处理即终止,不会调用TranslateMessge()和DispatchMessage()来翻译和分发消息给相应的窗口;若返回值为FALSE,才会调用翻译和分发消息函数。

该函数是MFC消息控制流最具特色的地方,它是CWnd类的虚拟函数,通过重载这个函数,我们可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来。

在win32程序中,关于消息有两种传递方式:

a.MFC消息,MFC会把所有的消息一条条放到一个AFX_MSGMAP_ENTRY结构中,形成一个数组,该数组存放了所有的消息和与它们相关的参数。也可以说是放到消息队列里去。

b. 采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息。

这两种方式中只有第一种(穿过消息队列的消息)才受PreTranslateMessage()影响,

第二种消息并不会理睬PreTranslateMessage()的存在。

 一、是否调用TranslateMessage()和DispatchMessage()是由一个名称为PreTranslateMessage()函数的返回值决定的,如果该函数返回TRUE,则不会把该消息分发给窗口函数处理。

二、传给PreTranslateMessage()的消息是未经翻译过的消息,它没有经过TranslateMessage()处理。可以在该函数中使用(pMsg->wParam==VK_RETURN)来拦截回车键。

三、在WindowProc里不能处理WM_Char消息。(WindowProc函数见MFC消息响应机制一文)

四、SetWindowText会发送WM_Char给窗口。

五、PeekMessage和GetMessage的区别:

    GetMessage在没有消息的时候等待消息,cpu当然低

    PeekMessage没有消息的时候立刻返回,所以cpu占用率高。因为游戏不能靠windows消息驱动,所以要用PeekMessage();

另一篇文章中:

  在一个WIN32程序中,WINDOWS会将消息传递给相应的窗口。但是消息不是立即就被传递给相应的窗口,而是会从整个程序最顶层的窗口传递到下一级窗口,再传递到下一级窗口,直到传递给目标窗口。在整个过程中,有些消息,在某些特定的情况下,无法默认传递到目标窗口的。比如用户在EDIT控件中按下回车键,CANCEL键等,如果EDIT窗口之前有对话框窗口,对话框会默认处理回车消息(即响应ONOK函数,然后关闭对话框),然后退出消息传递。所以EDIT会收不到。要解决这个问题,可以在EDIT窗口之前所有的对话框中重载PreTranslateMessage函数,然后在函数内加上:

if (pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN)     //如果消息类型为WM_KEYDOWN并且用户按下的是回车
  return FALSE;    //不翻译消息,直接将消息传递下去。具体可查MSDN。注意,这里返回值不能为TRUE,TRUE的意思是翻译消息后退出消息传递,如此一来虽然也能避开对话框默认处理,但是会退出消息传递,这样EDIT控件照样得不到消息。

如此,就可避开对话框默认处理,将消息传递下去。注意:只有对话框才会默认处理按下回车,CANCEL消息,其他控件窗口则不会,所以在其他窗口中不必重载PreTranslateMessage函数,当然如果重载了也不会错。

附:关于PreTranslateMessage()函数的小程序示例:

BOOL CSearchuserDlg::PreTranslateMessage(MSG* pMsg)
{

    if(pMsg->message==WM_KEYDOWN)  //判断是否有按键按下
    {
       switch(pMsg->wParam)
       {
          case VK_DOWN:     //表示是方向键中的向下的键

              //add handle code here

          break;

          case VK_UP:      //表示是方向键中的向上的键

               //add handle code here

          break;

          default:

          break;

       }

   }

}

**********************************************************************************************************************

1 楼mydo(侯佩|hopy|侯佩,偶滴人啊) 回复于 2005-02-14 13:06:56 得分 0
好像在文本框的属性中可以设置吧,另外可以单独处理
回车消息。Top

2 楼trtrsoft() 回复于 2005-02-14 13:26:23 得分 0
pretransmessage 函数中处理回车的消息Top

3 楼kugou123(酷狗)(君看一叶舟,出没风波里) 回复于 2005-02-14 13:51:09 得分 0
防止对话框程序按Enter和ESC退出的对策
http://www.xiaozhou.net/cooldog/blogview.asp?logID=24&cateID=13

一般情况下编写的对话框程序,用户在运行的时候,如果不注意按下了ENTER或者ESC键,程序就会立刻退出,之所以会这样,是因为按下Enter键时,Windows就会自动去找输入焦点落在了哪一个按钮上,当获得焦点的按钮的四周将被点线矩形包围。如果所有按钮都没有获得输入焦点,Windows就会自动去寻找程序或资源所指定的默认按钮(默认按钮边框较粗)。如果对话框没有默认按钮,那么即使对话框中没有OK按钮,OnOK函数也会自动被调用,对于一个普通的对话框程序来说,OnOK函数的调用,以为着程序会立刻退出。为了使Enter键无效,最简单的办法就是将CExDlg的OnOK函数写成空函数,然后针对OK按钮写一个新的函数来响应。ESC键的原理也是如此,它是默认和OnCancel函数映射在一起的。对于ESC键,需要自己重载CDialog类的PreTranslateMessage函数,当发现是ESC键的时候,过滤掉这个消息或者是替换掉这个消息。

一下是简单的代码示例:

【方法1】
可以先重载OnOK函数
voidCTestDlg::OnOK()
{ //里面什么也不写}

然后重载PreTranslateMessage函数
把ESC键的消息,用RETURN键的消息替换,这样,按ESC的时候,也会执行刚才的OnOK函数,这样问题就可以解决了。

BOOL CxxxDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_ESCAPE)
{
pMsg->wParam=VK_RETURN; //将ESC键的消息替换为回车键的消息,这样,按ESC的时候
//也会去调用OnOK函数,而OnOK什么也不做,这样ESC也被屏蔽
}
return CDialog::PreTranslateMessage(pMsg);

}

【方法2】

直接在重载的PreTranslateMessage函数中屏蔽回车和ESC的消息,和以上方法大同小异:

BOOL CxxxDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_ESCAPE) return TRUE;
if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN) return TRUE;
else
return CDialog::PreTranslateMessage(pMsg);
}
Top

4 楼rainfall19831109(恩恩) 回复于 2005-02-14 14:40:00 得分 0
添加PreTranslateMessage函数,增加一下代码:
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN)
{
return true;
}
}Top

5 楼luxingjyp(无涯) 回复于 2005-02-16 01:20:09 得分 0
正解楼上Top

6 楼Jiangcr81(晨风) 回复于 2005-02-16 08:30:48 得分 0
BOOL CTest002Dlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN)
{
return true;
}
if(pMsg->wParam==VK_ESCAPE)
{
return true;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Top

7 楼signoft(晴天) 回复于 2005-02-16 10:00:32 得分 0
如果是新加入的EDIT控件,看看是否选择EDIT控件的Multiline属性为TRUE;如果这个属性为FALSE,在控件中输入回车对话框就会关闭。Top

8 楼ayanamiwww(咩~咩『抵制日货』) 回复于 2005-02-16 11:17:55 得分 0
最简单的方法,删除“OK”按钮Top

9 楼abcde0xq(小千) 回复于 2005-05-22 11:01:02 得分 0
学习~Top

10 楼newzk(zk) 回复于 2005-05-22 11:49:13 得分 0
取消OK按钮的Default属性,在属性页内设置Top



*********************************************************************************************************************
用VC实现特定编辑框上对回车键响应
摘要 :本文讲述了在指定的编辑框上能响应从键盘输入回车键的一种方法,对进程内消息的解析、动态获取指定资源ID等技术也作了简要描述。

关键字 :Microsoft Visual C++ 6.0、编辑框、回车键、消息、资源

一、引言

在通常的以CEditView为基类的单文档/多文档视图程序中,可以很好的响应键盘输入的回车键,只需比较最近两次的输入的字符,看看最新输入的字符是否内码是13(0x0d,回车键的内码)即可识别出来,而要单独把一个编辑框放入对话框中却根本不响应,这个看似简单的问题在实际应用中还是解决起来比较困难的。尤其是当一个充当表单录入的对话框上有若干个编辑框,这就要求在一个编辑框添完一项表单后用习惯的回车键将该编辑框上的数据读取到内存中去,并自动将光标移动到下一个编辑框中准备填写下一栏表单。无疑这种界面是十分人机友好的,使录入人员不必去执行每填一下表单就去按一下执行读入到缓存功能的按钮的烦琐操作。但上述功能的实现却并不象其演示的功能那样简单,下面本文就对这项技术的实现及附带的其他技术作简要的介绍。

二、不能响应回车键的原因分析

之所以在以CEditView作为基类的程序中可以响应回车键,是由于该程序的视类本身就是一个Edit控件,这就是问题的关键所在。CEditView作为CView的派生类能响应从键盘输入的各种消息,其中有和键盘输入相关的WM_CHAR、WM_KEYDOWN、WM_KEYUP等消息。我们就可以在这些消息的响应函数中灵活地设计程序去捕捉到回车键的输入,并执行响应的操作。

当我们将编辑框作为一个普通的控件放到对话框上时情况就发生了变化。在此我们以CFormView为例,它也是CView的一个派生类,视是一个Form窗体(即对话框),当放有编辑框的窗体有回车键输入时,由于只有编辑框可以接受从键盘输入的字符,所以当键盘按下时统统把消息都发给了编辑框(在Windows下每个窗口、按钮、编辑框都看作一个窗口,都可以接受消息),可以通过ClassWizard在"Object IDs"选中编辑框所对应的ID号,在右边的消息框中可以看出该编辑框并不能响应WM_CHAR等消息,只能用EN_CHANGE事件来做类似的响应。可当我们加入了对该事件的处理函数时,却又将回车键当作控制字符,当输入回车键并不会激发EN_CHANGE事件,也就是说用这种方法仍旧无法捕获回车键的输入。

三、拦截回车键的思路与方法

Windows操作系统下各个窗口、控件归根结底都是通过系统的各种各样的消息来相互协调、相互联系的,而我们所遇到的这个问题换到消息的角度说就是"如何使程序能响应在编辑框上输入的回车键所发出的消息",只要能响应到这个消息,剩下的工作都可以在消息处理函数中完成。所以有必要对Windows系统的消息机制做些了解。

每个Windows应用程序开始执行后,Windows都为该程序创建一个"消息队列(message queue)",用来存放邮寄给该程序可能创建的各种不同窗口的消息。消息队列中消息的结构(MSG)为:

typedef struct tagMSG { /*msg*/ HWND hwnd;//窗口句柄,标识接收消息的窗口。 UINT message;//消息标识号,如WM_TIMER等。WPARAM wParam;//消息参数,当为键盘消息时,表示虚拟键码如VK_RETURN等。LPARAM lParam;//消息参数。 DWORD time;//邮寄消息的时间。 POINT pt;//邮寄消息时的光标位置,用屏幕坐标表示。 }MSG; 在系统下最常用的消息循环是调用GetMessage()函数从消息队列中取出消息,然后调用DespatchMessage() 函数让系统把消息发送给窗口函数,一般情况下其结果是把窗口的所有消息都传送给窗口函数。但特殊情况下可以在GetMessage()函数获得消息而又没发送出去之前,通过TranslateMessage()函数可以中途对消息进行解析,可以对指定的消息进行拦截,拦截后即可以照样发送出去,也可以不继续发送,完成对该消息的拦截,下面代码是该过程的示例: MSG msg;while(GetMessage(&msg,NULL,NULL,NULL,NULL){TranslateMessage(&msg);…… //对拦截的消息进行处理DispathchMessage(&msg);} 由于按下回车键时把产生的消息加入到消息队列中了,也传给了编辑框,但仅仅是由于编辑框没有能力处理该消息而造成了无法对回车键的响应,所以可以在消息循环里在把消息发送到编辑框之前就对消息进行拦截,并对其进行处理。其效果同编辑框响应回车键是一样的,仅在时序上有所提前而已。上述代码是在SDK(Software Develope Kits)下使用的,在MFC(Microsoft Foundation Class)下早已对其进行了封装,可以通过重载虚函数PreTranslateMessage()对所关心的消息进行解析:

BOOL CTestView::PreTranslateMessage(MSG* pMsg) { if (WM_KEYFIRST <= pMsg-> message && pMsg-> message <= WM_KEYLAST) {if(pMsg-> wParam==VK_RETURN ) {UpdateData(TRUE);AfxMessageBox(m_Text); } } return CFormView::PreTranslateMessage(pMsg); } 在上面的代码中,首先将pMsg-> message所表示的消息同WM_KEYFIRST 和WM_KEYLAST比较,确定是键盘消息,然后通过消息参数pMsg-> wParam的值来判断是否是回车键(VK_RETURN,虚拟键码可以从SDK相关资料查到)。如是,则可以将已输入到编辑框中的字符读取到m_Text中,并将其显示出来

四、对编辑框的识别

前面已经可以对回车键响应了,可一个表单窗体有若干个编辑框,其各自的处理方式不尽相同,这就有必要对编辑框进行识别、对不同的编辑框做不同的处理。而且当按下回车键时必须保证只有当前有焦点的编辑框能完成对回车键的响应动作,否则也就失去了实际意义。

在Windows下的程序中,所有的资源都是有唯一标号的,使每个资源对象能唯一的区别于其他资源,所以我们可以通过资源ID来对编辑框做出区别,使之完成各自的响应处理。在Microsoft Visual C++ 6.0下可以通过"View"菜单的"ID= Resource Symboles…"查到指定ID的资源标识号的实际数值,如在本例中的两个编辑框IDC_EDIT1和IDC_EDIT2所对应的数值分别为1000和1001,对前面的解析消息的代码做些改动,主要如下所示:

……if(pMsg-> wParam==VK_RETURN ) { HWND hWnd=::GetFocus(); int iID=::GetDlgCtrlID(hWnd); if(iID==1000)//第一个编辑框的标识为1000{UpdateData(TRUE); AfxMessageBox(m_Text1);//显示第一个编辑框的内容} if(iID==1001) //第二个编辑框的标识为 1001{UpdateData(TRUE); AfxMessageBox(m_Text2);//显示第二个编辑框的内容} }…… 在此通过API函数::GetFocus()(注意前面的"::",标识是全局API函数,而非某个类中的成员函数)取得当前光标所处的(即有焦点的)编辑框的句柄,然后通过API函数::GetDlgCtrlID()根据这个句柄返回此窗口资源的ID 号,该ID号是动态获取的,使之同预先查看好的编辑框的ID作下比较即可区分出是需要哪个编辑框对回车键作出响应。

小结:

本文通过对消息的解析实现了对特定编辑框的回车键的响应,在对消息机制有了基本的了解之后,可以用与本文类似的方法,对代码稍作改动,就可以使其他一些不能响应特殊消息的控件能接收、处理特定的消息。

本文来自: 乘风原创程序(http://www.qqcf.com) 详细出处参考:http://study.qqcf.com/web/480/84699.htm
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值