我为了移动一个无标题栏的窗体,使用了WM_NCHITTEST消息,这个消息大概如下:

通常,我们拖动对话框窗口的标题栏来移动窗口,但有时候,我们想通过鼠标在客户区上拖动来移动窗口。

一个容易想到的方案是,处理鼠标消息WM_LBUTTONDOWN和WM_LBUTTONUP。在OnLButtonUp函数中计算鼠标位置的变化,调用MoveWindow实现窗口的移动。

注意,拖动标题栏移动窗口的时候,会出现一个矩形框,它提示了窗口移动的当前位置。当鼠标左键放开的时候,窗口就移动到矩形框所在位置。而我们的实现方案中没有这个功能。

要实现此功能,我们必须自己来画这些矩形。

事实上,我们没有必要自己来做这件事情,因为Windows已经给我们做好了。

试想,如果我能够欺骗Windows,告诉它现在鼠标正在拖动的是标题栏而不是客户区,那么窗口移动操作就由Windows来代劳了。

要欺骗Windows并不像想像中的困难,甚至非常简单。

我们利用一个消息:WM_NCHITTEST。

MSDN对它的解释是:

The WM_NCHITTEST message is sent to a window when the cursor moves, or when a mouse button is pressed or released. If the mouse is not captured, the message is sent to the window beneath the cursor. Otherwise, the message is sent to the window that has captured the mouse.

这个消息是当鼠标移动或者有鼠标键按下时候发出的。

Windows用这个消息来做什么? “HITTEST”就是“命中测试”的意思,WM_NCHITTEST消息用来获取鼠标当前命中的位置。

WM_NCHITTEST的消息响应函数会根据鼠标当前的坐标来判断鼠标命中了窗口的哪个部位,消息响应函数的返回值指出了部位,例如它可能会返回HTCAPTION,或者HTCLIENT等。(其返回值有很多,请查阅MSDN)。

为了便于理解,我先描述一下Windows对鼠标键按下的响应流程:

1. 确定鼠标键点击的是哪个窗口。Windows会用表记录当前屏幕上各个窗口的区域坐标,当鼠标驱动程序通知Windows鼠标键按下了,Windows根据鼠标的坐标确定它点击的是哪个窗口。

2. 确定鼠标键点击的是窗口的哪个部位。Windows会向鼠标键点击的窗口发送WM_NCHITTEST消息,来询问鼠标键点击的是窗口的哪个部位。(WM_NCHITTEST的消息响应函数的返回值会通知Windows)。通常来说,WM_NCHITTEST消息是系统来处理的,用户一般不会主动去处理它(也就是说,WM_NCHITTEST的消息响应函数通常采用的是Windows默认的处理函数)。

3. 根据鼠标键点击的部位给窗口发送相应的消息。例如:如果WM_NCHITTEST的消息响应函数的返回值是HTCLIENT,表示鼠标点击的是客户区,则Windows会向窗口发送WM_LBUTTONDOWN消息;如果WM_NCHITTEST的消息响应函数的返回值不是HTCLIENT(可能是HTCAPTION、HTCLOSE、HTMAXBUTTON等),即鼠标点击的是非客户区,Windows就会向窗口发送WM_NCLBUTTONDOWN消息。

我们有必要详细讨论一下:如果WM_NCHITTEST的消息响应函数的返回值是HTCAPTION,即指示了鼠标点击了标题栏,接下去Windows的处理是怎样的?

上面已经提到,接下来,Windows会向窗口发送WM_NCLBUTTONDOWN消息。

MSDN对WM_NCLBUTTONDOWN消息描述如下:

WM_NCLBUTTONDOWN

nHittest = (INT) wParam; // hit-test value

pts = MAKEPOINTS(lParam); // position of cursor

WM_NCLBUTTONDOWN的wParam指示了鼠标点击的窗口部位,lParam指示了当前鼠标的坐标。

如果应用程序没有对该消息响应,则由系统默认处理。

系统默认处理又是怎样的呢?系统发现wParam指示了鼠标点击的是标题栏,就会标识当前窗口处于“拖拽状态”(Windows内部记录了每个窗口的状态信息)。由于标识了“拖拽状态”,则从此刻起到鼠标键放开之前,你的鼠标移动状况完全由Windows跟踪。它根据鼠标的移动,使得窗口作“同步”移动。

注意,这个过程中,窗口不会收到WM_NCMOUSEMOVE消息,因为窗口和鼠标是“同步”移动的,你的鼠标相对于窗口是静止的。

但问题同时也出现了, 我想在右键这个窗体的时候弹出一个菜单, 当我完成 MSG_WM_RBUTTONDOWN 这个消息的时候,发现窗体收不到这个消息, 将WM_NCHITTEST消息的实现去掉就可以了,看了一原因是:

因为你在WM_NCHITTEST中处理了鼠标消息,把他定位成HTCAPTION,也就是鼠标在标题栏上,而标题栏属于非客户区(NC); 
非客户区的事件消息都是以WM_NC开头的。也就是说,当你的WM_NCHITTEST返回HTCAPTION时,原来可以用WM_LBUTTONUP处理的消息,你只能用WM_NCLBUTTONUP来处理。

解决方法:

同时处理WM_NCHITTEST和WM_NCRBUTTONUP,而不处理WM_RBUTTONUP

关于WM_NCHITTEST消息的更多相关文章

  1. [转]关于WM_NCHITTEST消息

    http://www.cnblogs.com/GnagWang/archive/2010/09/12/1824394.html 我为了移动一个无标题栏的窗体,使用了WM_NCHITTEST消息,这个消 ...

  2. 对WM_NCHITTEST消息的了解+代码实例进行演示(消息产生消息,共24个枚举值)

    这个消息比较实用也很关键,它代表非显示区域命中测试.这个消息优先于所有其他的显示区域和非显示区域鼠标消息.其中lParam参数含有鼠标位置的x和y屏幕坐标,wParam 这里没有用. Windows应 ...

  3. 深刻:截获windows的消息并分析实例(DefWindowProc),以WM_NCHITTEST举例(Windows下每一个鼠标消息都是由 WM_NCHITTEST 消息产生的,这个消息的参数包含了鼠标位置的信息)

    1,回调函数工作机制 回调函数由操作系统自动调用,回调函数的返回值当然也是返回给操作系统了. 2,截获操作系统发出的消息,截获到后,将另外一个消息返回给操作系统,已达到欺骗操作系统的目的. 下面还是以 ...

  4. QT中异形窗口的绘制(winEvent处理WM_NCHITTEST消息)

    这里讨论的只是Windows平台上的实现. 在QT中绘制异形窗口,只要设定 windowFlag 为 CustomizeWindowHint,再结合setMask()就可以做出各种奇形怪状的窗口.相对 ...

  5. Delphi对WM_NCHITTEST消息的处理

    前提:WM_NCHITTEST是很重要的,只要鼠标在活动,Windows无时无刻在发这个消息进行探测. ------------------------------------------------ ...

  6. Delphi 实现无窗口移动(发WM_NCHITTEST消息计算,然后再发WM_SYSCOMMAND消息,带参数SC_DRAGMOVE)

    procedure imgListMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) ...

  7. 【笨嘴拙舌WINDOWS】键盘消息,鼠标消息

    键盘消息 Windows系统无论何时只有一个窗口(可能是子窗口,也就是控件)能获得焦点. 焦点窗口通过windows消息来响应人的键盘操作,与键盘相关的常用消息罗列如下: WM_KEYDOWN   按 ...

  8. windows 编程 —— 消息与参数(滚动条、键盘、鼠标)

    目录: 滚动条 键盘 鼠标 滚动条ScrollBar 发送消息:WM_VSCROLL和WM_HSCROLL 参数wParam:wParam消息参数被分为一个低字组和一个高字组.wParam的低字组是一 ...

  9. Windows消息大全

    最近在写TabControl的用户控件,需要用到sendMessage,已做备份. 引用:http://bbs.aau.cn/forum.php?mod=viewthread&tid=7776 ...

随机推荐

  1. android屏幕适配之度量单位、屏幕分类、图标尺寸归类分析

    好久没有做android项目UI的适配了,好多基本概念都已经模糊了,于是萌生了将屏幕分辨率.常用单位.常用图标尺寸等信息规整的想法,一下就是通过查询资料,自己验证的一些随笔,如有失误之处,望大家及时予 ...

  2. PXE+kickstart自动安装ubuntu14.04

    本文参考了诸多文章,先感谢这些文章的作者. 使用pxe安装系统需要安装dhcp,tftp,http等服务(当然也可以使用其他文件共享方式比如nfs,ftp). 实验环境: 1. vmware 12 2 ...

  3. EasyUI DataGrid 添加 Footer

    做后台管理界面时,EasyUI 的 DataGrid 经常会被用到,有时候一些总的统计数据不合适放在数据表格里,需要单独显示,这时候就可以放在Footer中显示而不必另外布局. 该怎么给 DataGr ...

  4. spring-事务实现原理

    spring事务的实现原理:aop. aop的两种实现方式: 1.动态代理: 事务方法与调用方法不能在同一个类中,否则事务不生效.解决方案,自己注入自己(实质注入的是代理类). 实现方式:jdk动态代 ...

  5. [Poi2015]

    [POI2015]Łasuchy 一看以为是sb题 简单来说就是每个人获得热量要尽量多 不能找别人 首先这道题好像我自己找不到NIE的情况 很容易想到一个优化 如果一个数/2>另一个数 那么一定 ...

  6. Redis 的几种数据结构&五种数据类型对象

    先看几种数据结构 通过分析底层的数据结构,学习如何根据场景选型和设计 1,简单动态字符串 redis使用的字符串SDS有别于C语言中的字符串 a, 结构 free字段为已分配但未使用的空间 len为已 ...

  7. IE9 浏览器无法捕获中键事件

    在silverlight中由于需要添加中键事件,所以通过hook来捕获,但是在IE9上无法运行,该问题是由于IE9设置问题,如下图修改配置, 取消选择即可:

  8. Scala 字段定义

    Scala 中定义字段只有val, var两种方式,都要在定义的同时即赋值,var 可以用占位符' _ '代替. 抽象类中定义的抽象字段不用赋初值,抽象方法也不用写方法体.(在子类中给抽象字段赋值和实 ...

  9. NSXMLParser自定义的一个xml解析工具

    // // DenglXMLParser.h // #import <Foundation/Foundation.h> @interface DenglXMLParser : NSXMLP ...

  10. sqlserver负载均衡

    http://www.cnblogs.com/gaizai/p/3644510.html