_TrackMouseEvent 与 WIN32 ToolTip

前言:我之所以将这两个部分放在一起写,当然是因为他们是有关系的啦,我们的窗口是不会响应WM_MOUSELEAVE和WM_MOUSEHOVER消息的,我们可以通过_TrackMouseEvent来激活这两个消息,让我们的窗口接收这两个消息,接收到消息后,我们能干嘛呢????注意WM_MOUSEHOVER消息,当我们在一个控件上停一段时间后,就会激发这个消息,这个消息应该叫“悬停”,所以,在鼠标悬停时,我们应该创建toolTip!!!!这就是两者的关系。我们先看_TrackMouseEvent是如何激活这两个消息的,然后再看怎样添加ToolTip。
先看个最终界面图,吸引下大家


一、_TrackMouseEvent

默认情况下,窗口是不响应 WM_MOUSELEAVE 和 WM_MOUSEHOVER 消息的,所以要使用_TrackMouseEvent函数来激活这两个消息。调用这个函数后,当鼠标在指定窗口上停留超过一定时间或离开窗口后,该函数会 Post 这两个消息到指定窗口。

1、添加头文件和库

在stdafx.h中,添加所需要的头文件和库文件,代码如下:

#include <CommCtrl.h>
#pragma   comment(lib,   "comctl32.lib")

2、使用方法

(1)在对话框类中定义一个变量来标识是否追踪当前鼠标状态,之所以要这样定义是要避免鼠标已经在窗体之上时,一移动鼠标就不断重复产生 WM_MOUSEHOVER 。并初始化为false,意思是尚未开始追踪!

bool m_bMouseTracking;//追踪WM_MOUSELEAVE 和 WM_MOUSEHOVER 消息
(2)在WM_MOUSEMOVE中开启追踪
   case WM_MOUSEMOVE:
	   {
		   // Start tracking this entire window again...
		   if( !m_bMouseTracking ) {//如果没有跟踪消息,则开启跟踪WM_MOUSEHOVER和WM_MOUSELEAVE消息
			   TRACKMOUSEEVENT tme = { 0 };
			   tme.cbSize = sizeof(TRACKMOUSEEVENT);
			   tme.dwFlags = TME_HOVER | TME_LEAVE;
			   tme.hwndTrack = m_hWnd;// 指定要 追踪 的窗口 
			   tme.dwHoverTime = 10;// 鼠标在按钮上停留超过 10ms ,才认为状态为 HOVER
			   _TrackMouseEvent(&tme);// 开启 Windows 的 WM_MOUSELEAVE , WM_MOUSEHOVER 事件支持 
			   m_bMouseTracking = true;//为true表示已经开启追踪,false表示现在未跟踪
		   }
}
(3)WM_MOUSEHOVE、WM_MOUSELEAVE中的处理
   case WM_MOUSEHOVER:
	   {
		   m_bMouseTracking=false;
	   }
	   return true;
   case WM_MOUSELEAVE:
	   {
		   if( m_bMouseTracking ) ::SendMessage(m_hWnd, WM_MOUSEMOVE, 0, (LPARAM) -1);
		   m_bMouseTracking = false;
	   }
	   break;

二、WIN32 ToolTip创建

上面我们完成了对WM_MOUSEHOVER和WM_MOUSELEAVE的追踪,但我们没有做任何的实际工作,我们使用_TrackMouseEvent只是开启了对这两个消息的接收,在接收到消息后,并没有做什么,下面我们就在接收到WM_MOUSEHOVER悬停消息后,创建ToolTip(提示文字)

(1)创建两个变量

	HWND m_hwndTooltip;//TOOLTIP的句柄
	TOOLINFO m_ToolTip;
讲解:
m_hwndTooltip:保存创建的tooltip窗体的句柄;
m_ToolTip:保存tooltip窗体的创建信息;

(2)创建显示

在需要创建显示TOOLTIP的地方,按照下面的代码,创建就会直接显示出来;

		   // Create tooltip information
		   CStdString sToolTip = L"HAHA HERE   XXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXX";
		   if( sToolTip.IsEmpty() ) return true;
		   ::ZeroMemory(&m_ToolTip, sizeof(TOOLINFO));
		   //m_ToolTip.cbSize = sizeof(TOOLINFO);
		   m_ToolTip.cbSize=TTTOOLINFOA_V2_SIZE;
		   m_ToolTip.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
		   m_ToolTip.hwnd = m_hWnd;
		   m_ToolTip.uId = (UINT_PTR) m_hWnd;
		   m_ToolTip.hinst = m_hInstance;
		   m_ToolTip.lpszText = const_cast<LPTSTR>( (LPCTSTR) sToolTip );
		   m_ToolTip.rect =pHover->GetPos();//不用指定显示位置,系统会自动判断位置
		   if( m_hwndTooltip == NULL ) {
			   m_hwndTooltip = ::CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hWnd, NULL, m_hInstance, NULL);
			   ::SendMessage(m_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM) &m_ToolTip);
			   ::SendMessage(m_hwndTooltip, TTM_SETMAXTIPWIDTH, 0, 20); //要多行显示,要先加上这个,然后在要分段的地方加上\n
		   }
		   ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, TRUE, (LPARAM) &m_ToolTip);
讲解:
1、创建步骤:
首先填充TOOLINFO结构体;在填充时,m_ToolTip.rect是不必填充的,系统会自动根据当前的鼠标位置显示TOOLTIP;
                          然后SendMessage---TTM_ADDTOOL,增加TOOLTIP到系统中,也就是让系统创建TOOLTIP窗体;
                          最后SendMessage---TTM_TRACKACTIVE,激活当前的TOOLTIP,也就是让其显示出来

2、m_ToolTip.cbSize的填充必须注意!!!!----直接影响到会不会显示出来,这部分会引用另一篇文章在下面讲解(参见:四、参考 ---2);
有些同鞋,根据上面的创建步骤死活就是显示不出来,为什么呢!!!!!m_ToolTip.cbSize赋值时出错了!!!!

如果#define _WIN32_WINNT 0x0500赋值方式必须是:

m_ToolTip.cbSize = sizeof(TOOLINFO)
而如果_WIN32_WINNT >= 0x0501,对应的赋值方式必须是:
m_ToolTip.cbSize=TTTOOLINFOA_V2_SIZE;
3、多行显示
多行显示必须有两个步骤才能完成:
(1)在显示的字符串中,将要分段的地方用“\n”分隔;
(2)添加代码:

::SendMessage(m_hwndTooltip, TTM_SETMAXTIPWIDTH, 0, 20); //要多行显示,要先加上这个,然后在要分段的地方加上\n

(3)撤销显示

在需要撤销显示的位置,添加上下面这行代码,取消TOOLTIP的激活状态

if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip);

三、源码讲解(最后有源码下载)

我们在《WIN32界面开发之四:CPaintManagerUI类构建》的基础上进行修改,利用这些教程逐步逼近DUILIB,争取把DUILIB一步步实现,如果对上篇还不了解的同学,还是先看看DUIRudiment是怎么构架的吧。突然到一节来估计是看不懂的。按照上面的内容,大家也应该知道,我们这里要添加的功能就是添加_TrackMouseEvent和TOOLTIP

(1)、修改CPaintManagerUI

1、为CPaintManagerUI添加m_hInstance变量;
大家从TOOLTIP的创建也可以知道,创建ToolTip是要当前应用程序的hInstance的,所以我们要先保存起来,与DUILIB一致,添加静态变量:

static HINSTANCE m_hInstance;//注意在类外初始化
和一个设置函数:
static void SetInstance(HINSTANCE hInst);
设置函数的实现:
void CPaintManagerUI::SetInstance(HINSTANCE hInst)
{
    m_hInstance = hInst;
}
2、初始化m_hInstance
在WinMain()函数的开头,调用CPaintManagerUI::SetInstance()

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	CPaintManagerUI::SetInstance(hInstance);

	CStartPage *startPage=new CStartPage();

	startPage->SetInstance(hInstance);
	startPage->Create();
	startPage->ShowWindow(true);

	Message();

	delete startPage;

	Gdiplus::GdiplusShutdown(gdiplusToken);
	return 0;
}

(2)TOOLTIP创建

在WM_MOUSEHOVER中创建TOOLTIP,先看代码:

   case WM_MOUSEHOVER:
	   {
		   m_bMouseTracking = false;

		   POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
		   CControlUI* pHover =m_pRoot->FindCtrlFromPT(pt);
		   if( pHover == NULL ) break;
		   // Generate mouse hover event
		   if( m_pEventHover != NULL ) {
			   TEventUI event = { 0 };
			   event.ptMouse = pt;
			   event.Type = UIEVENT_MOUSEHOVER;
			   event.pSender = m_pEventHover;
			   event.dwTimestamp = ::GetTickCount();
			   m_pEventHover->Event(event);
		   }//这段与创建TOOLTIP无关,这里是给控件发送HOVER事件
		   // Create tooltip information
		   CStdString sToolTip = L"HAHA HERE   XXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXX\nXXXXXXXXXXXXXXXXXXXXXXX";
		   if( sToolTip.IsEmpty() ) return true;
		   ::ZeroMemory(&m_ToolTip, sizeof(TOOLINFO));
		   //m_ToolTip.cbSize = sizeof(TOOLINFO);
		   m_ToolTip.cbSize=TTTOOLINFOA_V2_SIZE;
		   m_ToolTip.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
		   m_ToolTip.hwnd = m_hWnd;
		   m_ToolTip.uId = (UINT_PTR) m_hWnd;
		   m_ToolTip.hinst =m_hInstance;
		   m_ToolTip.lpszText = const_cast<LPTSTR>( (LPCTSTR) sToolTip );
		   m_ToolTip.rect =pHover->GetPos();//不用指定显示位置,系统会自动判断位置
		   if( m_hwndTooltip == NULL ) {
			   m_hwndTooltip = ::CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hWnd, NULL,m_hInstance, NULL);
			   ::SendMessage(m_hwndTooltip, TTM_ADDTOOL, 0, (LPARAM) &m_ToolTip);
			   ::SendMessage(m_hwndTooltip, TTM_SETMAXTIPWIDTH, 0, 20); //要多行显示,要先加上这个,然后在要分段的地方加上\n
		   }
		   ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, TRUE, (LPARAM) &m_ToolTip);
	   }
	   return true;
讲解:
1、在WM_MOUSEHOVER消息中,我们先给当前控件发送MOUSEHOVER事件
2、创建并显示TOOLTIP,由于在我的targetver.h 文件中定义的_WIN32_WINNT是0x0600;所以我这里的赋值方式为:m_ToolTip.cbSize=TTTOOLINFOA_V2_SIZE;

#ifndef _WIN32_WINNT            // 指定要求的最低平台是 Windows Vista。
#define _WIN32_WINNT 0x0600     // 将此值更改为相应的值,以适用于 Windows 的其他版本。
#endif

(3)移出时撤销显示

这里有两个地方要撤销显示,首先当鼠标移出控件时,要撤销显示,再者,在移出当前窗体时,也要撤销显示;
1、鼠标移出控件时的代码:
在WM_MOUSEMOVE中,在移出控件时,添加SendMessage---TTM_TRACKACTIVATE , FALSE

		   if( pNewHover != m_pEventHover && m_pEventHover != NULL ) {
			   event.Type = UIEVENT_MOUSELEAVE;
			   event.pSender = m_pEventHover;
			   m_pEventHover->Event(event);
			   //在鼠标离开控件时,要取消激活TOOLTIP,也就是让其不显示!
			   if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip);
			   m_pEventHover = NULL;
		   }
2、鼠标移出窗体时

由于我们用_TrackMouseEvent监听的HWND是窗体的HWND,所以鼠标在移出窗体时,会触发WM_MOUSELEAVE消息;所以在WM_MOUSELEAVE同样添加撤销代码:

   case WM_MOUSELEAVE:
	   {
		   if( m_hwndTooltip != NULL ) ::SendMessage(m_hwndTooltip, TTM_TRACKACTIVATE, FALSE, (LPARAM) &m_ToolTip);
		   if( m_bMouseTracking ) ::SendMessage(m_hWnd, WM_MOUSEMOVE, 0, (LPARAM) -1);
		   m_bMouseTracking = false;
	   }

四、参

1、MSDN关于TOOLLIP的各项参数及用法讲解:http://msdn.microsoft.com/en-us/library/windows/desktop/bb760246%28v=vs.85%29.aspx

2、《 不靠谱的tooltip》,讲解了_WIN32_WINNT值与tooltip.cbSize的关系;这也就是大部分创建不成功的原因所在;网址: http://blog.csdn.net/problc/article/details/6654601

《不靠谱的tooltip》摘

typedef struct tagTOOLINFOA {
    UINT cbSize;
    UINT uFlags;
    HWND hwnd;
    UINT_PTR uId;
    RECT rect;
    HINSTANCE hinst;
    LPSTR lpszText;
#if (_WIN32_IE >= 0x0300)
    LPARAM lParam;
#endif
#if (_WIN32_WINNT >= 0x0501)
    void *lpReserved;
#endif
} TTTOOLINFOA, NEAR *PTOOLINFOA, *LPTTTOOLINFOA;

如果#define _WIN32_WINNT 0x0500,那么tooltip一切正常
当有一天#define _WIN32_WINNT 0x0501了,
这时设置tooltip.cbSize = sizeof(TOOLINFO)的时候就让人崩溃了。
编译调试不显示任何错误,但是tooltip就是显示不出来了。。。(they were just failing silently without knowing why)
因为系统默认加载comctl 5.82,这个版本里面的tooltip的size根本没有sizeof(TOOLINFO),里面没有void *lpReserved;
所以cbSize设大了,出错了。。。
这时需要写成 tooltip.cbSize = TTTOOLINFOA_V2_SIZE;
或者强制指定comctl 6.0
#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'\"")

ps:  指定#define _WIN32_WINNT 0x0501将导致程序只能运行在xp及以上系统。

五、最后

本文由HARVIC完成,如若转载,请标明出处,请大家尊重初创者的版权,谢谢!!

源文地址:http://blog.csdn.net/harvic880925/article/details/9796229

源码地址:http://download.csdn.net/detail/harvic880925/5881347

声明:感谢金山影音漂亮的界面图片,该图片来自网络。

你可能感兴趣的:(_TrackMouseEvent 与 WIN32 ToolTip)