您的位置:首页 > 其它

【WIN32之旅】给托盘图标加上MOUSE_ENTER、MOUSE_HOVER、MOUSE_LEAVE消息

2016-03-12 09:52 946 查看
转载请说明原出处,谢谢~ /article/7859363.html

相信很多人在WIN32项目开发过程中涉及到托盘相关处理时都会碰到这样一个问题,无法获取托盘图标的鼠标移入(Mouse Enter)移出(Mouse Leave)以及悬停(Mouse Hover)消息,因为WINDOWS只提供了一个WM_MOUSEMOVE消息给我们,想要实现更为复杂的界面逻辑,就比如自绘的ToolTip提示或者消息预览窗口那可怎么办呢?

说到这里,我想大伙们心底里一定都异口同声地表示揪心,不过没关系,我们这群爱捣鼓的也不吃吃素的人,让我们先来分析一下这几个消息的核心产生因素,我们知道时间是线性的,而程序的执行也是随时间呈线性的,所以我们不妨从时间上来分析我们需要的这几个消息与WM_MOUSEMOVE之间的关系:

PS. 首先我们要明白鼠标只要在托盘图标RECT区域中移动就会产生WM_MOUSEMOVE消息。

1.WM_MOUSE_ENTER(ENTER在时间上的表现为鼠标从LEAVE转变为ENTER,即第一次收到WM_MOUSEMOVE时)

2.WM_MOUSE_HOVER (HOVER在时间上的表现为间隔一定时长鼠标HOVER位置不变,即鼠标位置等于最后一次收到WM_MOUSEMOVE时的位置)

3.WM_MOUSE_LEAVE(LEAVE则正好与ENTER过程相反,即鼠标从ENTER转为了LEAVE状态)

从上面我们的分析我们可将在时间上呈线性的WM_MOUSEMOVE消息分解出我们所需要的WM_MOUSE_ENTERWM_MOUSE_HOVERWM_MOUSE_LEAVE消息,因此我们只需要利用上WIN32的定时器,无论托盘图标是在底部工具栏上还是托盘溢出区上,这都是一种比通过各种复杂方法获取托盘图标RECT区域,或者使用单独线程进行检测更为简便的方法。

下面就上关键代码吧:

/*******************************************************************************
*  @file      TrayIconHandler.hpp 2016\3\10 17:47:38 $
*  @author    Jeffrey Tse <JeffreyTse.mail@gmail.com>
*  @brief     Trayicon message handle program.
******************************************************************************/

#define  TRAYICON_HOVER_TIMER_ID         0x024
#define  TRAYICON_LEAVE_TIMER_ID         0x025

class TrayIconHandler
{
public:
enum _TrayIconMessage {
WM_MOUSE_ENTER = 0x00F0,
WM_MOUSE_HOVER,
WM_MOUSE_LEAVE
} TrayIconMessage

public:
TrayIconHandler(HWND hWnd, UINT uTrayIconMsg, UINT uHoverElapse = 300U)
: m_hWnd(hWnd), m_uTrayIconMsg(uTrayIconMsg), m_uHoverElapse(uHoverElapse), m_bMouseEnter(FALSE){};
~TrayIconHandler()
{
// 清除检测定时器
KillTimer(m_hMainHwnd, TRAYICON_HOVER_TIMER_ID);
KillTimer(m_hMainHwnd, TRAYICON_LEAVE_TIMER_ID);
};

LRESULT MessageNotify(TrayIconMessage uMsg)
{
// Do something ...
}

LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HRESULT lRes = S_OK;

switch (uMsg)
{
case m_uTrayIconMsg: lRes = OnTrayIconMessage(uMsg, wParam, lParam); break;
case WM_TIMER:       lRes = OnTimer(uMsg, wParam, lParam); break;
default:
break;
}

return lRes;
}

LRESULT OnTrayIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(lParam)
{
case WM_RBUTTONDOWN:
{
}
break;
case WM_LBUTTONDBLCLK:
{
}
break;
...
case WM_MOUSEMOVE:
{
if(!m_bMouseEnter)
{
m_bMouseEnter = TRUE;
// DEBUG_STRING(_T("################# TRAYICON_ENTER #################\r\n"))
// Do something ...
MessageNotify(TrayIconMessage::WM_MOUSE_ENTER);
}

POINT pt;
if(GetCursorPos(&pt)) // 获取当前鼠标位置
{
if(pt.x != m_ptMouseHover.x || pt.y != m_ptMouseHover.y)
{
// 重置检测定时器
KillTimer(m_hMainHwnd, TRAYICON_HOVER_TIMER_ID);
KillTimer(m_hMainHwnd, TRAYICON_LEAVE_TIMER_ID);
SetTimer(m_hMainHwnd, TRAYICON_HOVER_TIMER_ID, m_uHoverElapse, NULL);
SetTimer(m_hMainHwnd, TRAYICON_LEAVE_TIMER_ID, 200U, NULL);

// 记录鼠标位置
m_ptMouseHover = pt;
}
}
}
break;
return S_OK;

}

LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(wParam == TRAYICON_HOVER_TIMER_ID)
{
KillTimer(m_hMainHwnd, TRAYICON_HOVER_TIMER_ID);
POINT pt;
if(GetCursorPos(&pt))
{
if(pt.x == m_ptMouseHover.x && pt.y == m_ptMouseHover.y)
{
// DEBUG_STRING(_T("################# TRAYICON_HOVER #################\r\n"))
// Do something ...
MessageNotify(TrayIconMessage::WM_MOUSE_HOVER);
}
}
}
else if(wParam == TRAYICON_LEAVE_TIMER_ID)
{
POINT pt;
if(GetCursorPos(&pt))
{
if(pt.x != m_ptMouseHover.x || pt.y != m_ptMouseHover.y)
{
m_bMouseEnter = FALSE;
KillTimer(m_hMainHwnd, TRAYICON_HOVER_TIMER_ID);
KillTimer(m_hMainHwnd, TRAYICON_LEAVE_TIMER_ID);
// DEBUG_STRING(_T("################# TRAYICON_LEAVE #################\r\n"))
// Do something ...
MessageNotify(TrayIconMessage::WM_MOUSE_LEAVE);
}
}
}

return S_OK;
}

private:
HWND  m_hMainHwnd;     // 主消息窗口
UINT  m_uTrayIconMsg;  // 注册的托盘通知消息
UINT  m_uHoverElapse;  // 停留时长
BOOL  m_bMouseEnter;   // 鼠标是否进入
POINT m_ptMouseHover;  // 鼠标停留位置
}


使用示例代码:

#include <windows.h>
...

TrayIconHandler  handler(g_hWnd, WM_TRAYICON_NOTIFY);

...

// 窗口过程函数
LRESULT CALLBACK WindowProc(  HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam )
{

switch (uMsg)
{
case WM_DESTROY:      // 窗口销毁消息
PostQuitMessage( 0 ); // 发送退出消息
return 0;
}

// 调用处理windows消息
handler.HandleMessage(uMsg, wParam, lParam);

return DefWindowProc( hwnd, uMsg, wParam, lParam );
}


总而言之,虽然微软没有给我们所需要的检测鼠标进入、移出以及悬停托盘的消息,但只要我们静下心来认真思考仔细分析,我想我们总会会心一笑入浴春风,相信以上的内容能够帮助到正需要帮助的人。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: