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

来源:互联网 发布:新蛋现在怎么样 知乎 编辑:程序博客网 时间:2024/05/23 05:09

转载请说明原出处,谢谢~ http://blog.csdn.net/seven_1992/article/details/50848071


    相信很多人在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         0x025class TrayIconHandler{public:    enum _TrayIconMessage {        WM_MOUSE_ENTER = 0x00F0,        WM_MOUSE_HOVER,        WM_MOUSE_LEAVE    } TrayIconMessagepublic:    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 );}


    总而言之,虽然微软没有给我们所需要的检测鼠标进入、移出以及悬停托盘的消息,但只要我们静下心来认真思考仔细分析,我想我们总会会心一笑入浴春风,相信以上的内容能够帮助到正需要帮助的人。

1 0