如果鼠标在窗体除客户区域外的部分引发鼠标事件的话,窗体就会收到一个非客户区域消息。非客户区域由边框、菜单栏、标题栏、滚动条、系统菜单、最小化、最大化组成。
非客户区域消息主要是为了系统自身使用,比如,当热点移到窗口边框上时,系统用非客户区域消息把鼠标光标变为两个箭头的光标。窗体必须传递非客户区域消息给DefWindowProc函数,以便利用内置的鼠标处理接口。
对于客户区域的鼠标消息来说,也有一个非客户区域的鼠标消息对应。名字也很类似,只不过非客户区域消息的常量中包含了“NC”。例如,非客户区域内移动光标将产生WM_NCMOUSEMOVE消息,按下鼠标左键将产生WM_NCLBUTTONDOWN消息。
非客户区域消息的lParam参数是包含热点x坐标及y坐标的结构,同客户区域坐标系不同,该坐标是以屏幕坐标系来表示的。屏幕坐标系的(0,0)点表示屏幕的左上角。
wParam参数包含一个hit-test值,hit-test用来指出非客户区域消息是在哪引发的。下面就会对hit-test作详细介绍。
只要鼠标事件产生,系统就会发送一个WM_NCHITTEST消息到鼠标热点所在窗口或鼠标捕获的窗口。系统根据该消息确定应该发送一个客户区域消息还是非客户区域消息。要想接收鼠标移动及鼠标键消息,就要求应用程序必须在调用DefWindowProc函数时传递WM_NCHITTEST消息。
WM_NCHITTEST消息的lParam参数包含鼠标热点的屏幕坐标,DefWindowProc函数会检查该坐标,并返回一个hit-test值,其中包含了热点的位置。hit-test值可以为下列值之一:
值 | 热点所在位置 |
---|---|
HTBORDER | 不能改变大小的窗口的边框 |
HTBOTTOM | 下边框 |
HTBOTTOMLEFT | 边框的左下角 |
HTBOTTOMRIGHT | 边框的右下角 |
HTCAPTION | 标题栏 |
HTCLIENT | 客户区域 |
HTCLOSE | 关闭按钮 |
HTERROR | 屏幕背景或窗口间的分界线上(与HTNOWHERE相同,但不包括DefWindowProc函数引发beep的错误的位置) |
HTGROWBOX | 缩放格(同HTSIZE) |
HTHELP | 帮助按钮 |
HTHSCROLL | 水平滚动条 |
HTLEFT | 窗口的左边框 |
HTMENU | 菜单中 |
HTMAXBUTTON | 最大化按钮 |
HTMINBUTTON | 最小化按钮 |
HTNOWHERE | 屏幕背景或窗口间的分界线上 |
HTREDUCE | 最小化按钮 |
HTRIGHT | 窗口的右边框 |
HTSIZE | 缩放格(同HTGROWBOX). |
HTSYSMENU | 系统菜单或子窗体的关闭按钮上 |
HTTOP | 窗口的上边框 |
HTTOPLEFT | 边框的左上角 |
HTTOPRIGHT | 边框的右上角 |
HTTRANSPARENT | 在被同一线程的另一个窗口覆盖的窗口上 |
HTVSCROLL | 垂直滚动条 |
HTZOOM | 最大化按钮 |
如果鼠标光标在窗口的客户区域内,DefWindowProc将返回HTCLIENT给窗口处理过程。窗口处理过程把该值返回给系统,然后系统转换热点的屏幕坐标为客户区域坐标,接着再投递相应的客户区域消息。
当热点在窗口的非客户区域内时,DefWindowProc函数就会返回其他的hit-test值之一,当窗口处理过程反馈该值时,系统就会投递非客户区域消息,其中把该hit-test值放在wParam参数内,光标坐标放在lParam参数内。
Windows Me及WindowsXP中增加了鼠标测距的功能,该功能会在用户按下、释放CTRL键时在热点周围显示几个同心圆。本功能可以让用户在杂乱(或者分辨率变高后)的桌面上迅速定位鼠标指针,在一个低劣的显示器上,可以减少对用户视力的损害。更多信息,请参照SystemParametersInfo的如下标志:
SPI_GETMOUSESONAR
SPI_SETMOUSESONAR
Windows Me及WindowsXP中包含了隐藏鼠标的功能,这可以使得用户打字时隐藏鼠标。移动鼠标的话就会重新出现,只要是输入文本,鼠标就会保持不显示,例如,输入邮件或其他文档时。详细信息,请参照SystemParametersInfo的如下标志:
SPI_GETMOUSEVANISH
SPI_SETMOUSEVANISH
鼠标滚轮组合了滚轮及键的作用,滚轮上有不连续的均匀分布的槽口,当旋转滚轮时,每遇到一个槽口就会产生一个滚轮消息。滚轮键可以作为一个普通的Windows中键(或者说第三键)使用。按下或释放鼠标滚轮将发送标准的WM_MBUTTNUP及WM_MBUTTONDOWN消息,双击的话发送WM_MBUTTONDBLCLK消息。
Windows 95, Windows NT 3.51: 通过作为IntelliMousepointingdevice的单独运行模块产生的MSH_MOUSEWHEEL消息提供鼠标滚轮支持。
Windows 98/Me, Windows NT 4.0 and later: WM_MOUSEWHEEL消息提供滚轮支持。
在大多数情况下,应用程序显式的处理WM_MOUSEWHEEL与MSH_MOUSEWHEEL消息,然而这两个消息还是有以下重要区分:
下列章节分别概述了的两个消息处理过程:
Windows 98/Me, Windows NT 4.0 and later:通过WM_MOUSEWHEEL消息提供旋转滚轮支持。
旋转滚轮将向获得焦点的窗口发送WM_MOUSEWHEEL消息。DefWindowProc函数会把消息传递给父窗体。由于DefWindowProc按照父窗体链依次传递该消息,直到有一个窗体处理,所以不应该再有内部转寄的消息了。
应该使用SystemParametersInfo函数得到每次滚动(滚轮槽口)一个文档滚动的行数。应用程序通过下述调用就可以得到滚动的行数:
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, pulScrollLines,0)
"pulScrollLines"变量指向一个unsignedinteger值,其中包含滚轮旋转时暗示的行数:
滚动行数默认为3,如果要修改该值,可以通过控制面板中的鼠标属性来修改,操作系统用指定为SPI_SETWHEELSCROLLLINES的WM_SETTINGCHANGE消息到所有顶级窗口,然后就可以调用SystemParametersInfo得到新的滚动行数了:
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, pulScrollLines, 0)
下表中包含了可以提供滚动的控件(其中包含用户设置的可滚动行):
控件 | 滚动 |
---|---|
Edit Control | 垂直、水平 |
List box Control | 垂直、水平 |
Combo box | 没有下拉列表时,每次向前或向后滚动,有下拉列表时,每次滚动都把消息转寄给列表框,从而实现滚动。 |
CMD (命令行) | 垂直 |
Tree View | 垂直、水平 |
List View | 垂直、水平 |
Up/down Scrolls | 一次一个item |
Trackbar Scrolls | 一次一个item |
Microsoft Rich Edit 1.0 | 垂直,注意,Exchange客户端有自己的listview、treeview控件,因此不支持滚轮。 |
Rich Edit 2.0 | 自从Microsoft Office 97 |
要确定鼠标是否带滚轮,可以调用GetSystemMetrics(SM_MOUSEWHEELPRESENT),如果返回TRUE就说明带。
下面多行编辑器控件的窗口处理过程示例:
Windows 95, Windows NT3.51:通过一个单独运行的模块MSWheel提供滚轮支持,它可以产生MSH_MOUSEWHEEL消息。MSWheel模块,由MSWheel.exe及MSWheel.dll组成,是安装IntelliMousepointingdevice时包含在其软件部分中的。MSH_MOUSEWHEEL是在Zmouse.h中作为一个字符串定义的。
应用程序调用RegisterWindowMessage以产生消息ID,带此ID的一个消息就会通过MSWhell投递给当前滚轮滚动的那个前台窗口。RegisterWindowMessage函数用来产生一个唯一的新窗口消息值,当调用SendMessage或PostMessage函数时就会用到该值。头部文件(ZMouse.h)包含了传给RegisterWindowMessage的串。
要确定滚轮是否可用,可以使用HwndMsWheelinline函数,或发送一个请求给MSWheel模块。Zmouse.h中可以找到HwndMsWheel,它不仅返回滚动行数,也注册消息、到MSWheel窗体的句柄,是否支持滚轮的标志。如果发送请求给MSWheel模块,如果应答为TRUE,就说明有滚轮。应用程序可以添加以下代码段以发送请求:
通过使用GetForegroudWindow函数返回的窗口句柄,MSWheel模块可以把滚轮消息投递给前台窗口。前台窗口必须决定是否处理该消息,或交给支持滚轮的控件或内置OLE对象处理。
如果前台窗口需要传递滚轮消息,它可以使用PostMessage或SendMessage来实现。对于内置支持滚轮的控件,你必须决定是否应用程序应该使用SendMessage函数并判断返回值。
对于内置的OLE应用程序来说,请使用PostMessage。由内置OLE的运行模式决定没有必要检查返回值,因为容器把消息提交给服务器,然后就会“忘记”该消息,由服务器应用程序决定是否处理。
对于OLE服务器来说(包括文档类服务器),把处理滚轮消息的代码交给窗口处理过程,当传递消息给内置的OLE应用程序时,建议通过IOleInPlaceObject:GetWindow得到相应的窗口句柄。
内置应用程序必须确保处理了MSH_MOUSEWHEEL消息,尤其需要注意的是,应用程序是内置的时,界面不会在frame窗口呈现,所以处理消息的代码是在frame窗口的处理过程中的话,消息不会被处理。例如,用MicrosoftWord及MicrosoftExcel时,由于文档窗口就是最高层的内建窗口,因此那也是需要处理消息的地方。
通过在IntelliPoint的滚轮设置中,可以设置每次滚动(一个槽口)所滚动的行数,或者控制面板的鼠标属性中可以设置。
要得到当前设置的行数,可以使用以下代码请求MSWheel模块:
另外,也可以使用内联函数HwnMsWheel,ZMouse.h包含其函数声明,该函数不仅返回滚动行数,而且还包括已注册的消息、MSWheel窗口的句柄、支持滚轮的标志。
如果变更了滚动行数,MSWheel就会调用以下代码通过广播把消息传给所有窗体:
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETWHEELSCROLLLINES, NULL);
当一个应用程序收到该消息后,就会向MSWheel模块发送一个消息,以得到滚动行数:
SendMessage(hdlMSHWheel, msgMSHWheelGetScrollLines,0, 0).
当设置滚动行数时,用户可以选择滚动一页,而不一定非得指定滚动行数,如果是这样的话,就会用到WHEEL_PAGESCROLL,如果返回该值,滚动操作应该像每次单击滚动条时一样滚动一页。rollone page at a time.
滚动行函数在MSWheel发行版019中提供完备的支持。
当用户单击一个非激活的顶级窗体,或非激活的顶级窗体的子窗体时,系统就会发送WM_MOUSEACTIVATE消息(还包括其他消息)给顶级窗体或子窗体,该消息在WM_NCHITTEST消息之后,但在button-down消息之前。当把WM_MOUSEACTIVATE消息交给DefWindowProc函数处理时,系统激活该顶级窗口并投递button-down消息。
通过处理WM_MOUSEACTIVATE,一个窗体可以就像鼠标单击一样控制激活顶级窗口,并确定该窗体是否接收后续的button-down消息。处理WM_MOUSEACTIVATE后,通过返回以下值实现该目的:
值 | 含义 |
---|---|
MA_ACTIVATE | 激活窗体,但不删除鼠标消息。 |
MA_NOACTIVATE | 不激活窗体,也不删除鼠标消息。 |
MA_ACTIVATEANDEAT | 激活窗体,删除鼠标消息。 |
MA_NOACTIVATEANDEAT | 不激活窗体,但删除鼠标消息。 |