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

转载请说明原出处,谢谢~ 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 
 *  @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 
...

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 );
}


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

你可能感兴趣的:(WINDOWS,C/C++)