在Windows应用程序开发中,处理鼠标事件和实现拖拽操作是常见的需求。本文将介绍如何通过使用SetCapture API和TrackMouseEvent API来实现这些功能。
鼠标事件处理是用户界面交互的重要组成部分。以下是一些常见的使用场景:
SetCapture API的行为相对复杂,且在平台SDK中的文档不够详尽。理解以下限制有助于更好地使用SetCapture:
有两种类型的捕获,称之为前景捕获和背景捕获。
前景捕获在满足以下两个条件时获得:
否则(如果当前线程不是前台线程,或没有鼠标按钮被按下),窗口仅获得背景捕获。
如果鼠标按钮全部释放,鼠标捕获将自动回退到背景。
前景捕获和背景捕获的区别如下:
在应用程序中实现拖拽操作,需要实现以下消息处理:
有两种变体的API可用:
如果可以忽略Windows 95作为目标,则使用TrackMouseEvent()。如果需要针对Windows 95,并且可以假设机器至少安装了IE3,则使用_TrackMouseEvent()。如果需要在Windows 95上使用TrackMouseEvent()功能,并且不能假设至少安装了IE3,则以下快速演示了基本功能。
完整的自定义实现TrackMouseEvent需要实现一个消息钩子,以便它可以钩住任何窗口的消息。任何需要检测鼠标进入、离开或悬停事件的窗口都可以使用以下代码:
#define TID_POLLMOUSE 100
#define MOUSE_POLL_DELAY 500
case WM_MOUSEMOVE:
SetTimer(hwnd,TID_POLLMOUSE,MOUSE_POLL_DELAY,NULL);
break;
case WM_TIMER:
RECT rc;
POINT pt;
GetWindowRect(hwnd,&rc);
GetCursorPos(&pt);
if(PtInRect(&rc,pt))
{
PostMessage(hwnd,WM_MOUSEHOVER,0,0L);
break;
}
PostMessage(hwnd,WM_MOUSELEAVE,0,0L);
KillTimer(hwnd,TID_POLLMOUSE);
break;
通过使用SetCapture()可以更快地检测到鼠标离开,从而实现更响应的版本。
#define TID_POLLMOUSE 100
#define MOUSE_POLL_DELAY 500
case WM_MOUSEMOVE:
RECT rc;
POINT pt;
GetWindowRect(hwnd,&rc);
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
if(PtInRect(&rc,pt))
{
SetTimer(hwnd,TID_POLLMOUSE,MOUSE_POLL_DELAY,NULL);
if(hwnd != GetCapture())
{
SetCapture(hwnd);
PostMessage(hwnd,WM_MOUSEENTER,0,0L);
}
break;
}
ReleaseCapture();
KillTimer(hwnd,TID_POLLMOUSE);
PostMessage(hwnd,WM_MOUSELEAVE,0,0L);
break;
case WM_TIMER:
GetWindowRect(hwnd,&rc);
GetCursorPos(&pt);
if(PtInRect(&rc,pt))
{
PostMessage(hwnd,WM_MOUSEHOVER,0,0L);
break;
}
ReleaseCapture();
KillTimer(hwnd,TID_POLLMOUSE);
PostMessage(hwnd,WM_MOUSELEAVE,0,0L);
break;
除了鼠标按下或初始拖拽操作开始检测外,大多数应用程序都在模态循环中实现其拖拽代码,以防止主应用程序窗口过程变得混乱。
还要注意,大多数系统拖拽操作允许用户在拖拽过程中通过按下另一个按钮并释放初始按钮来“交换”按钮。如果拖拽操作没有在模态循环中实现,这种情况需要特别处理,以防止启动另一个拖拽操作。