转载:https://blog.twofei.com/667/
自绘或子类化控件时,有时需要处理鼠标进入(MouseEnter)/鼠标离开(MouseLeave)/鼠标停留(MouseHover)消息,虽然有定义这两个消息常量(WM_MOUSEHOVER 与 WM_MOUSELEAVE),但默认情况下 Windows 的窗口消息是不会产生这三个消息的。但提供了一个 API 函数_TrackMouseEvent 可以使用,这个函数可以使 Windows 产生 鼠标停留(WM_MOUSEHOVER)与 鼠标离开(WM_MOUSELEAVE)消息,至于鼠标进入消息(Windows 没有定义 WM_MOUSEENTER 宏),直接手动在第一次鼠标移动的时候模拟产生就行了。不过不爽的是 鼠标离开消息 Windows 没有传递离开时的坐标过来,所以这个也得手动模拟:每次鼠标移动的时候记录下坐标,在收到鼠标离开消息时,采用此值即可。下面的代码没有作此处理,如有需要可手动加上。
效果:
.h文件
#ifndef _POPUPWND_H_ #define _POPUPWND_H_ #include "WndShadow.h" #define WIN_VISITWIDTH 280 #define WIN_VISITHEIGHT 186 #define ID_VISITPOP_WINDOW 20 #define ID_TIMER_DISPLAY_DELAY 30 #define ID_TIMER_CLOSE_WINDOW 40 class CPopUpWnd :public WindowImplBase { public: CPopUpWnd(); ~CPopUpWnd(); public: virtual LPCTSTR GetWindowClassName() const; virtual void OnFinalMessage(HWND hWnd); virtual CDuiString GetSkinFile(); virtual CDuiString GetSkinFolder(); virtual UILIB_RESOURCETYPE GetResourceType() const; virtual CControlUI* CreateControl(LPCTSTR pstrClassName); virtual LRESULT ResponseDefaultKeyEvent(WPARAM wParam); virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); virtual void Notify(TNotifyUI& msg); virtual void OnClick(TNotifyUI& msg); virtual void InitWindow(); void OnPrepare(); public: void InitControl(); void StartTrack(); void MoveSelfWindow(); LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam,BOOL& bHandled); LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnMouseHover(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); LRESULT OnMouseEnter(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); public: CWndShadow m_WndShadow; CLabelUI* m_pMousePos; UINT m_uTime;//移动窗口时器 protected: bool m_bMouseTracking; // 是否正在跟踪鼠标 bool m_bMouseWithin; // 鼠标是否在窗口内 }; #endif//_POPUPWND_H
.cpp文件
#include "....stdafx.h" #include "PopUpWnd.h" #pragma comment(lib, "comctl32.lib") CPopUpWnd::CPopUpWnd() :m_uTime(0) ,m_bMouseTracking(false) ,m_bMouseWithin(false) ,m_pMousePos(NULL) { } CPopUpWnd::~CPopUpWnd() { if (m_uTime) { KillTimer(this->GetHWND(),ID_VISITPOP_WINDOW); m_uTime = 0; } } LPCTSTR CPopUpWnd::GetWindowClassName() const { return _T("PopUpWnd"); } void CPopUpWnd::OnFinalMessage(HWND hWnd) { CWindowWnd::OnFinalMessage(hWnd); } CDuiString CPopUpWnd::GetSkinFile() { return _T("xml//PopUpDlg.xml"); } CDuiString CPopUpWnd::GetSkinFolder() { return _T(""); } UILIB_RESOURCETYPE CPopUpWnd::GetResourceType() const { return UILIB_FILE; } LRESULT CPopUpWnd::ResponseDefaultKeyEvent(WPARAM wParam) { if (wParam == VK_RETURN) { return FALSE; } else if (wParam == VK_ESCAPE) { Close(); return TRUE; } return FALSE; } CControlUI* CPopUpWnd::CreateControl(LPCTSTR pstrClassName) { return NULL; } void CPopUpWnd::InitWindow() { //窗体增加阴影 CWndShadow::Initialize(m_PaintManager.GetInstance()); m_WndShadow.Create(m_hWnd , true); m_WndShadow.SetSize(2); m_WndShadow.SetPosition(1, 1); //修改窗口风格 //SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_hWnd,GWL_STYLE) & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME ); //加入WS_EX_LAYERED扩展属性 LONG styleValue = ::GetWindowLong(m_hWnd, GWL_EXSTYLE); styleValue &= ~(WS_EX_APPWINDOW);//当窗口可见时将一个顶层窗口放置在任务栏上 styleValue |= WS_EX_TOOLWINDOW; //工具条窗口样式 SetWindowLong(m_hWnd,GWL_EXSTYLE,styleValue); InitControl(); //右下角慢慢弹出 //m_uTime = SetTimer(m_hWnd,ID_VISITPOP_WINDOW,20,NULL); //在此调用MoveSelfWindow()方法 窗口不会显示在右下角,因为InitWindow在WM_CREATE 消息响应函数里调用,窗口还没创建出来 } void CPopUpWnd::InitControl() { m_pMousePos = static_cast<CLabelUI*>(m_PaintManager.FindControl(TEXT("mouse"))); } void CPopUpWnd::OnPrepare() { //直接显示在右下角 MoveSelfWindow(); //3秒后窗口自动关闭 SetTimer(m_hWnd,ID_TIMER_DISPLAY_DELAY,3000,NULL); } void CPopUpWnd::Notify(TNotifyUI& msg) { if( msg.sType == _T("windowinit") ) OnPrepare(); if ( msg.sType == _T("click") ) { } WindowImplBase::Notify(msg); } void CPopUpWnd::OnClick(TNotifyUI& msg) { __super::OnClick(msg); } void CPopUpWnd::MoveSelfWindow() { RECT rect; SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0); int y=rect.bottom-rect.top; int x=rect.right-rect.left; x=x-WIN_VISITWIDTH; y=y-WIN_VISITHEIGHT; MoveWindow(m_hWnd,x,y,WIN_VISITWIDTH,WIN_VISITHEIGHT,TRUE); } LRESULT CPopUpWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam,BOOL& bHandled) { static int nHeight=0; int cy=GetSystemMetrics(SM_CYSCREEN); int cx=GetSystemMetrics(SM_CXSCREEN); RECT rect; SystemParametersInfo(SPI_GETWORKAREA,0,&rect,0); int y=rect.bottom-rect.top; int x=rect.right-rect.left; x=x-WIN_VISITWIDTH; switch(wParam) {//多个定时器 case ID_VISITPOP_WINDOW: if(nHeight<=WIN_VISITHEIGHT) { ++nHeight; MoveWindow(m_hWnd,x,y-WIN_VISITHEIGHT,WIN_VISITWIDTH,WIN_VISITHEIGHT,TRUE); } else { //nHeight = 0; KillTimer(m_hWnd,ID_VISITPOP_WINDOW); SetTimer(m_hWnd,ID_TIMER_DISPLAY_DELAY,3000,NULL); } break; case ID_TIMER_DISPLAY_DELAY: KillTimer(m_hWnd,ID_TIMER_DISPLAY_DELAY); Close(IDCLOSE); break; } return 0; } //LRESULT CPopUpWnd::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) //{ // POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; // //::ScreenToClient(m_hWnd,&pt);//转换成屏幕坐标 // //POINT pt1 ;/*= { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };*/ // //GetCursorPos(&pt1); // RECT rect; // ::GetWindowRect (m_hWnd,&rect); // //::ClientToScreen(m_hWnd,&pt);//转换成屏幕坐标 // if (PtInRect(&rect,pt))//判断point是否在rect内部 // { // KillTimer(m_hWnd,ID_TIMER_DISPLAY_DELAY); // } // // return 0; //} void CPopUpWnd::StartTrack() { TRACKMOUSEEVENT tme = {0}; tme.cbSize = sizeof(tme); tme.hwndTrack = m_hWnd; tme.dwFlags = TME_HOVER | TME_LEAVE; tme.dwHoverTime = HOVER_DEFAULT; _TrackMouseEvent(&tme); } LRESULT CPopUpWnd::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // 如果鼠标不在客户区内,就产生一个鼠标进入事件 if(!m_bMouseWithin) { OnMouseEnter(uMsg /* WM_MOUSEMOVE */, wParam, lParam, bHandled); } // 开始跟踪鼠标停留/离开事件 if(!m_bMouseTracking) { StartTrack(); m_bMouseTracking = true; return 0; } TCHAR text[MAX_PATH]; wsprintf(text,_T("鼠标移动: %d,%d"),GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); m_pMousePos->SetText(text); return 0; } LRESULT CPopUpWnd::OnMouseHover(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { m_bMouseTracking = false; TCHAR text[MAX_PATH]; wsprintf(text,_T("鼠标停留: %d,%d"),GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); m_pMousePos->SetText(text); return 0; } LRESULT CPopUpWnd::OnMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { m_bMouseTracking = false; m_bMouseWithin = false; SetTimer(m_hWnd,ID_TIMER_DISPLAY_DELAY,3000,NULL); TCHAR text[MAX_PATH]; wsprintf(text,_T("鼠标离开: %d,%d"),GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); m_pMousePos->SetText(text); return 0; } LRESULT CPopUpWnd::OnMouseEnter(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { m_bMouseWithin = true; KillTimer(m_hWnd,ID_TIMER_DISPLAY_DELAY); TCHAR text[MAX_PATH]; wsprintf(text,_T("鼠标进入:%d,%d"),GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); m_pMousePos->SetText(text); return 0; } LRESULT CPopUpWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lRes = 0; BOOL bHandled = TRUE; switch( uMsg ) { case WM_NCLBUTTONDBLCLK: //不做处理 { return 0; } break; case WM_TIMER: lRes = OnTimer(uMsg,wParam,lParam,bHandled); break; case WM_MOUSELEAVE: lRes = OnMouseLeave(uMsg,wParam,lParam,bHandled); break; default: bHandled = FALSE; } return WindowImplBase::HandleMessage(uMsg,wParam,lParam); }