Windows ToolTips简要介绍
发布日期:2021-05-07 23:35:45 浏览次数:23 分类:原创文章

本文共 31358 字,大约阅读时间需要 104 分钟。

Windows 标准控件ToolTips简要介绍


参考文档 MSDN


https://msdn.microsoft.com/en-us/library/ff486072(v=vs.85).aspx




一,什么是ToolTips


ToolTips 就是一个类似于一个悬浮的文本框,在鼠标指针移动上去能显示特定的文本。


Screen shot showing text in a tooltip that appears over a file on the desktop


Screen shot showing a tooltip containing one line of text, positioned above a button on a dialog box


Screen shot showing a tooltip with an icon, title, and text, positioned below a button on a dialog box


Screen shot showing a tooltip containing a file name positioned next to a file icon in a tree control


各种ToolTips样式。


二,创建ToolTips


HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,                            WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,                            CW_USEDEFAULT, CW_USEDEFAULT,                            CW_USEDEFAULT, CW_USEDEFAULT,                            hwndParent, NULL, hinstMyDll,                            NULL);SetWindowPos(hwndTip, HWND_TOPMOST,0, 0, 0, 0,             SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);




此后ToolTips的窗口函数自动维护Tooltips的尺寸,位置和显示隐藏状态等。 ToolTips的高度基于所设置的字体的高度。




1.激活ToolTips


Tooltips可以处于激活和未激活的状态。激活状态下ToolTips会显示文本。 当ToolTips未激活,其文本讲不被显示。即使鼠标指针放在一个Tools上发送TTM_ACTIVE可以激活和关闭激活一个ToolTips的状态。




2.将ToolTips关联Tools


创建一个TOOLINFO的结构体对象,设置uID为关联工具的ID


设置uFlags为  TTF_IDISHWND


并发送TTM_ADDTOOL消息给ToolTips的句柄。后面的完整的例子。




3.显示文本


默认使用TOOLINFO的  lpszText为显示问题。 可以发送TTM_UPDATETIPTEXT消息来更新显示值。


如果将lpszText 设置为 LPSTR_TEXTCALLBACK ToolTips需要显示文本时候,会Call之前注册的父窗口句柄的窗口函数并发送 TTN_GETDISPINFO通知码,


该消息包含了指向NMTTDISPINFO 结构的指针用于修改相应的文本,以供后续显示使用。




4.消息和通知


windows默认只发送消息给包含鼠标指针的窗口,并不会发送消息给ToolTips。因此需要关联ToolTips和其对应的父窗口活控件ID来控制其显示(恰当的位置和恰当的时间)。


ToolTips会自动处理一下的消息。


1.通过TOOLINFO绑定过的控件或者父窗口的矩形区域。


2.绑定ToolTip的父窗口在同一个线程内。


满足以上两个条件 将TOOLINFO的uFlags设置为 TTF_SUBCLASS  然后发送TTM_ADDTOOL消息给Tooltip的句柄。  但是ToolTips和关联的窗口必须有直接的消息通路。也就是父窗口和子窗口的关系。 如果你关联了别的进程的窗口,还是收不到消息的。可能要使用HOOK。此时你应该发送TTM_RELAYEVENT消息给tooltip 参考Tracking Tooltip




当Tooltip要显示的时候会发送给其拥有者窗口TTN_SHOW通知码。 TTN_POP表明Tooltip即将要隐藏。  通过WM_NOTIFY消息发送。




三,ToolTips应用




1.一个简单的ToolTips的例子



#include <windows.h>#include <windowsx.h>#include <commctrl.h>#include "resource.h"#pragma comment(lib, "comctl32.lib")LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM);HINSTANCE g_hInst;HWND hTTWnd;int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int){	g_hInst = hInstance;	INITCOMMONCONTROLSEX cx = { sizeof(INITCOMMONCONTROLSEX), ICC_BAR_CLASSES };	BOOL ret = InitCommonControlsEx(&cx);	return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MyDlgProc);}// Description://   Creates a tooltip for an item in a dialog box. // Parameters://   idTool - identifier of an dialog box item.//   nDlg - window handle of the dialog box.//   pszText - string to use as the tooltip text.// Returns://   The handle to the tooltip.//HWND CreateToolTip(int toolID, HWND hDlg, PTSTR pszText){	if (!toolID || !hDlg || !pszText)	{		return FALSE;	}	// Get the window of the tool.	HWND hwndTool = GetDlgItem(hDlg, toolID);	// Create the tooltip. g_hInst is the global instance handle.	HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,		CW_USEDEFAULT, CW_USEDEFAULT,		CW_USEDEFAULT, CW_USEDEFAULT,		hDlg, NULL,		g_hInst, NULL);	if (!hwndTool || !hwndTip)	{		return (HWND)NULL;	}	// Associate the tooltip with the tool.	TOOLINFO toolInfo = { 0 };	toolInfo.cbSize = sizeof(toolInfo);	toolInfo.hwnd = hDlg;	toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;	toolInfo.uId = (UINT_PTR)hwndTool;	toolInfo.lpszText = pszText;	SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);	return hwndTip;}void CreateToolTipForRect(HWND hwndParent){	// Create a tooltip.	HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,		hwndParent, NULL, g_hInst, NULL);	SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0,		SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);	// Set up "tool" information. In this case, the "tool" is the entire parent window.	TOOLINFO ti = { 0 };	ti.cbSize = sizeof(TOOLINFO);	ti.uFlags = TTF_SUBCLASS;	ti.hwnd = hwndParent;	ti.hinst = g_hInst;	ti.lpszText = TEXT("This is your tooltip string.");	GetClientRect(hwndParent, &ti.rect);	// Associate the tooltip with the "tool" window.	SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);}LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){	switch (uMsg)	{	case WM_INITDIALOG:	{		CreateToolTipForRect(hDlg);		break;	}	case WM_CLOSE:		EndDialog(hDlg, FALSE);		break;	}	return FALSE;}






这个代码很简单就是在Windows对话框上显示ToolTips。可是编译以后死活不显示,初始化InitCommonControlsEx的调用也没有问题。观察到自己创建的对话框风格非常复古。






和MSDN上的大相径庭。






后来查阅相关资料。这是由于项目缺少了Manifest定义。在网上找了一个Manifest的定义文件在项目加载此文件就解决了此问题。关于Manifest文件的定义参考此文章


MSDN: Enable Visual Style in your program.


https://msdn.microsoft.com/en-us/library/windows/desktop/bb773175(v=vs.85).aspx#no_extensions




Windows.Manifest定义如下



  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>   <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">   <assemblyIdentity   name="Microsoft.Windows.XXXX"   processorArchitecture="x86"   version="5.1.0.0"   type="win32"/>   <description>Windows Shell</description>   <dependency>   <dependentAssembly>   <assemblyIdentity   type="win32"   name="Microsoft.Windows.Common-Controls"   version="6.0.0.0"   processorArchitecture="x86"   publicKeyToken="6595b64144ccf1df"   language="*"    />   </dependentAssembly>   </dependency>   </assembly>






运行结果如下。






关于这个问题深入研究发现,是调用TOOLINFOW类的时候如果程序加载Common Control 6.0以下的版本,这个结构体的定义的实际size比6.0少4个字节。


而ANSI版本无此问题。如果使用Unicode版本必须加入manifest强制让应用程序加载common Control 6.0才能使用sizeof(TOOLINFOW)的返回值。


否则就要将此值减去4


参考此文章:  https://stackoverflow.com/questions/2545682/unicode-tooltips-not-showing-up/15173051




2. 一个指定位置显示Tooltip的例子 同时显示2个Tooltip并且自己定位Toolpis的位置。



#include <windows.h>#include <windowsx.h>#include <commctrl.h>#include "resource.h"#pragma comment(lib, "comctl32.lib")LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM);HINSTANCE g_hInst;HWND hTTWnd;HWND g_hwndTrackingTT;HWND g_hwndTrackingTT1;TOOLINFO g_toolItem;BOOL g_TrackingMouse;int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int){	g_hInst = hInstance;	INITCOMMONCONTROLSEX cx = { sizeof(INITCOMMONCONTROLSEX), ICC_BAR_CLASSES };	BOOL ret = InitCommonControlsEx(&cx);	return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MyDlgProc);}// Description://   Creates a tooltip for an item in a dialog box. // Parameters://   idTool - identifier of an dialog box item.//   nDlg - window handle of the dialog box.//   pszText - string to use as the tooltip text.// Returns://   The handle to the tooltip.//HWND CreateToolTip(int toolID, HWND hDlg, PTSTR pszText){	if (!toolID || !hDlg || !pszText)	{		return FALSE;	}	// Get the window of the tool.	HWND hwndTool = GetDlgItem(hDlg, toolID);	// Create the tooltip. g_hInst is the global instance handle.	HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,		CW_USEDEFAULT, CW_USEDEFAULT,		CW_USEDEFAULT, CW_USEDEFAULT,		hDlg, NULL,		g_hInst, NULL);	if (!hwndTool || !hwndTip)	{		return (HWND)NULL;	}	// Associate the tooltip with the tool.	TOOLINFO toolInfo = { 0 };	toolInfo.cbSize = sizeof(toolInfo);	toolInfo.hwnd = hDlg;	toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;	toolInfo.uId = (UINT_PTR)hwndTool;	toolInfo.lpszText = pszText;	SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);	return hwndTip;}void CreateToolTipForRect(HWND hwndParent){	// Create a tooltip.	HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,		hwndParent, NULL, g_hInst, NULL);	SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0,		SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);	// Set up "tool" information. In this case, the "tool" is the entire parent window.	TOOLINFO ti = { 0 };	ti.cbSize = sizeof(TOOLINFO);	ti.uFlags = TTF_SUBCLASS;	ti.hwnd = hwndParent;	ti.hinst = g_hInst;	ti.lpszText = TEXT("This is your tooltip string.");	GetClientRect(hwndParent, &ti.rect);	// Associate the tooltip with the "tool" window.	SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);}HWND CreateTrackingToolTip(int toolID, HWND hDlg, WCHAR* pText){	// Create a tooltip.	HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,		hDlg, NULL, g_hInst, NULL);	if (!hwndTT)	{		return NULL;	}	// Set up the tool information. In this case, the "tool" is the entire parent window.	g_toolItem.cbSize = sizeof(TOOLINFO);	g_toolItem.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;	g_toolItem.hwnd = hDlg;	g_toolItem.hinst = g_hInst;	g_toolItem.lpszText = pText;	g_toolItem.uId = (UINT_PTR)hDlg;	GetClientRect(hDlg, &g_toolItem.rect);	// Associate the tooltip with the tool window.	SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&g_toolItem);	return hwndTT;}LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){	switch (uMsg)	{	case WM_INITDIALOG:	{		g_hwndTrackingTT = CreateTrackingToolTip(0, hDlg, L"");		g_hwndTrackingTT1 = CreateTrackingToolTip(0, hDlg, L"");		break;	}	case WM_MOUSELEAVE:		SendMessage(g_hwndTrackingTT, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&g_toolItem);		SendMessage(g_hwndTrackingTT1, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&g_toolItem);		g_TrackingMouse = FALSE;		return FALSE;	case WM_MOUSEMOVE:		static int oldX, oldY;		int newX, newY;		if (!g_TrackingMouse)   // The mouse has just entered the window.		{                       // Request notification when the mouse leaves.			TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT) };			tme.hwndTrack = hDlg;			tme.dwFlags = TME_LEAVE;			TrackMouseEvent(&tme);			// Activate the tooltip.			SendMessage(g_hwndTrackingTT, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&g_toolItem);			SendMessage(g_hwndTrackingTT1, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&g_toolItem);			g_TrackingMouse = TRUE;		}		newX = GET_X_LPARAM(lParam);		newY = GET_Y_LPARAM(lParam);		// Make sure the mouse has actually moved. The presence of the tooltip 		// causes Windows to send the message continuously.		if ((newX != oldX) || (newY != oldY))		{			oldX = newX;			oldY = newY;			// Update the text.			WCHAR coords[12];			wsprintf(coords, TEXT("%d, %d"), newX, newY);			g_toolItem.lpszText = coords;			SendMessage(g_hwndTrackingTT, TTM_SETTOOLINFO, 0, (LPARAM)&g_toolItem);			SendMessage(g_hwndTrackingTT1, TTM_SETTOOLINFO, 0, (LPARAM)&g_toolItem);			// Position the tooltip. The coordinates are adjusted so that the tooltip does not overlap the mouse pointer.			//POINT pt = { newX, newY };			POINT pt = { 50, 50 };			ClientToScreen(hDlg, &pt);			SendMessage(g_hwndTrackingTT, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 10, pt.y - 20));			SendMessage(g_hwndTrackingTT1, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 100, pt.y - 20));		}		return FALSE;	case WM_CLOSE:		EndDialog(hDlg, FALSE);		break;	}	return FALSE;}





运行结果,


可以自行设定ToolTips的位置TTM_TRACKPOSITION,修改ToolTips的显示值TTM_SETTOOLINFO, 控制Tooltips的显示TTM_TRACKACTIVE.




3. 显示多行文本的ToolTips


多行文本ToolTips参考图




使用 消息来创建一个多行文本的ToolTips。设置每行的宽度,超过此宽度的文本会自动换行。也可以使用\r\n 强制换行。


注意 的szText成员最多只能存储80个字符。如果要显示长字符串,请用NMTTDISPINFO的lpszText指向一个长文本的字符。


一下例子使用了TTN_GETDISPINFO通知码来修改tooltips的文本。



    case WM_NOTIFY:    {        switch (((LPNMHDR)lParam)->code)        {        case TTN_GETDISPINFO:            LPNMTTDISPINFO pInfo = (LPNMTTDISPINFO)lParam;            SendMessage(pInfo->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, 150);            wcscpy_s(pInfo->szText, ARRAYSIZE(pInfo->szText),                 L"This\nis a very long text string " \                L"that must be broken into several lines.");            break;        }        break;    }


测试用例


显示两个固定位置的多行提示框 若窗口处于非激活状态则隐藏。


提示框的位置会随着窗的移动而移动。



#include <windows.h>#include <windowsx.h>#include <commctrl.h>#include <stdio.h>#include "resource.h"#pragma comment(lib, "comctl32.lib")LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM);HINSTANCE g_hInst;HWND hTTWnd;HWND g_hwndTrackingTT;HWND g_hwndTrackingTT1;TOOLINFO g_toolItem;BOOL g_TrackingMouse = FALSE;int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, int){	g_hInst = hInstance;	INITCOMMONCONTROLSEX cx = { sizeof(INITCOMMONCONTROLSEX), ICC_BAR_CLASSES };	BOOL ret = InitCommonControlsEx(&cx);	return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MyDlgProc);}// Description://   Creates a tooltip for an item in a dialog box. // Parameters://   idTool - identifier of an dialog box item.//   nDlg - window handle of the dialog box.//   pszText - string to use as the tooltip text.// Returns://   The handle to the tooltip.//HWND CreateToolTip(int toolID, HWND hDlg, PTSTR pszText){	if (!toolID || !hDlg || !pszText)	{		return FALSE;	}	// Get the window of the tool.	HWND hwndTool = GetDlgItem(hDlg, toolID);	// Create the tooltip. g_hInst is the global instance handle.	HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,		CW_USEDEFAULT, CW_USEDEFAULT,		CW_USEDEFAULT, CW_USEDEFAULT,		hDlg, NULL,		g_hInst, NULL);	if (!hwndTool || !hwndTip)	{		return (HWND)NULL;	}	SetWindowPos(hwndTip, HWND_TOPMOST, 0, 0, 0, 0,		SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);	// Associate the tooltip with the tool.	TOOLINFO toolInfo = { 0 };	toolInfo.cbSize = sizeof(toolInfo);	toolInfo.hwnd = hDlg;	toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;	toolInfo.uId = (UINT_PTR)hwndTool;	toolInfo.lpszText = LPSTR_TEXTCALLBACK; // pszText;	SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);	return hwndTip;}void CreateToolTipForRect(HWND hwndParent){	// Create a tooltip.	HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,		hwndParent, NULL, g_hInst, NULL);	SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0,		SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);	// Set up "tool" information. In this case, the "tool" is the entire parent window.	TOOLINFO ti = { 0 };	ti.cbSize = sizeof(TOOLINFO);	ti.uFlags = TTF_SUBCLASS;	ti.hwnd = hwndParent;	ti.hinst = g_hInst;	ti.lpszText = TEXT("This is your tooltip string.");	GetClientRect(hwndParent, &ti.rect);	// Associate the tooltip with the "tool" window.	SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);}HWND CreateTrackingToolTip(int toolID, HWND hDlg, WCHAR* pText){	// Create a tooltip.	HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,		hDlg, NULL, g_hInst, NULL);	if (!hwndTT)	{		return NULL;	}	// Set up the tool information. In this case, the "tool" is the entire parent window.	g_toolItem.cbSize = sizeof(TOOLINFO);	g_toolItem.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;	g_toolItem.hwnd = hDlg;	g_toolItem.hinst = g_hInst;	g_toolItem.lpszText = LPSTR_TEXTCALLBACK;//pText;	g_toolItem.uId = (UINT_PTR)hDlg;	GetClientRect(hDlg, &g_toolItem.rect);	// Associate the tooltip with the tool window.	SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&g_toolItem);	SendMessage(hwndTT, TTM_SETDELAYTIME, (WPARAM)TTDT_AUTOPOP, (LPARAM)MAKELONG(30 * 1000, 0));	return hwndTT;}LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){	static BOOL bActive = FALSE;	switch (uMsg)	{	case WM_INITDIALOG:	{		g_hwndTrackingTT = CreateTrackingToolTip(0, hDlg, L"");		g_hwndTrackingTT1 = CreateTrackingToolTip(0, hDlg, L"");		//hTTWnd = CreateToolTip(IDCANCEL, hDlg, TEXT("IDCANCEL String \r\n nextline"));		//CreateToolTipForRect(hDlg);		break;	}	case WM_NOTIFY:	{		switch (((LPNMHDR)lParam)->code)		{		case TTN_GETDISPINFO: /*TTN_NEEDTEXT:*/			LPNMTTDISPINFO pInfo = (LPNMTTDISPINFO)lParam;			SendMessage(pInfo->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, 150);			wcscpy_s(pInfo->szText, ARRAYSIZE(pInfo->szText),				L"This\nis a very long text string " \				L"that must be broken into several lines.");			break;		}		break;	}	case WM_MOVE:	{		POINT pt = { 50, 50 };		ClientToScreen(hDlg, &pt);		SendMessage(g_hwndTrackingTT, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 10, pt.y - 20));		SendMessage(g_hwndTrackingTT1, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 10, pt.y + 80));	}		break;	case WM_ACTIVATE:		// if the main windows is inactive ,disappear the tooltips.		if (LOWORD(wParam) == WA_INACTIVE)		{			SendMessage(g_hwndTrackingTT, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&g_toolItem);			SendMessage(g_hwndTrackingTT1, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&g_toolItem);			bActive = FALSE;		}		else		{			SendMessage(g_hwndTrackingTT, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&g_toolItem);			SendMessage(g_hwndTrackingTT1, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&g_toolItem);			POINT pt = { 50, 50 };			ClientToScreen(hDlg, &pt);			SendMessage(g_hwndTrackingTT, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 10, pt.y - 20));			SendMessage(g_hwndTrackingTT1, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 10, pt.y + 80));			bActive = TRUE;		}		break;	case WM_MOUSEMOVE:	{		if (!bActive)			break;		SendMessage(g_hwndTrackingTT, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&g_toolItem);		SendMessage(g_hwndTrackingTT1, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&g_toolItem);		POINT pt = { 50, 50 };		ClientToScreen(hDlg, &pt);		SendMessage(g_hwndTrackingTT, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 10, pt.y - 20));		SendMessage(g_hwndTrackingTT1, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 10, pt.y + 80));	}		break;	case WM_CLOSE:		EndDialog(hDlg, FALSE);		break;	}	return FALSE;}









熟悉了ToolTips的用法可以对其用C++做一个封装方便调用。


自己实现了一个可以自行设定位置的基于Windows ToolTips的类。并且使用了SetWindowsLongPtr自行处理了WM_NOTIFY消息。(主窗口不必关心内部消息处理)


CToolTips.h  头文件



/*					CToolTips - CToolTips.h**		Author: Sesiria <stjohnson_free@hotmail.com>*		Copyright (c) 2017 Sesiria.**	CToolTips module header file*	This module is designed to to encapsulation the behavior of the standard Windows ToolTips control.*/#ifndef _CTOOLTIPS_H_#define _CTOOLTIPS_H_#include <windows.h>#include <CommCtrl.h>#include <map>#include <list>#include <string>class CToolTips  //Based on the Track style of the ToolTips control.{	// member function.public:	CToolTips(HWND hParentWnd, HINSTANCE hInstance, bool MultiLine = false); //Normal Style and Multiline	~CToolTips();	void setText(LPCTSTR szText);	void setMultiLineText(LPCTSTR szText, const LONG nWidth);	void initToolTips();	void setPosition(const POINT& pt);	void setVisible(bool bVisible);	void setUpdate();	static LRESULT CALLBACK tooltipWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);	static LRESULT CALLBACK parentWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);	WNDPROC getParentWndProc()	{		return m_parentWndProc;	};private:	//	void setActive(bool bActive);	void _registerWndProc();	void _unregisterWndProc();	bool _createToolTips();	void _destroyToolTips();	void _addtoInstanceTable();	void _removeFromInstanceTable();	// member variable.private:	LPTSTR m_szText;	LPTSTR m_multiText;	LONG m_nWidth;	bool m_bMultiLine;	bool m_bActive;	bool m_bVisible;	POINT m_pos;	HWND m_hParent;	HWND m_hToolTips;	HINSTANCE m_hInst;	TOOLINFO m_toolInfo;	WNDPROC m_parentWndProc;	WNDPROC m_tooltipWndProc;};#endif // _CTOOLTIPS_H_






CToolTips.cpp 源文件





/*					CToolTips - CToolTips.cpp**		Author: Sesiria <stjohnson_free@hotmail.com>*		Copyright (c) 2017 Sesiria.**	CToolTips module source file*	This module is designed to to encapsulation the behavior of the standard Windows ToolTips control.*/#include "CToolTips.h"#define DELETEP(p)      do { if (p) { delete(p); (p)=NULL; } } while (0)#define DELETEPV(pa)    do { if (pa) { delete [] (pa); (pa)=NULL; } } while (0)typedef std::list<HWND> ListInstance;// this data struct is used to support different parent dialog bind with the tooltips instance.// the HWND is the parent dialog HWND// ListInstance is a list container to store all the handle of the tooltips relative the same// parent dialog.typedef std::map<HWND, ListInstance*> TableInstance;/static TableInstance g_tblInstance;bool isInTable(HWND hParent){	TableInstance::iterator iter = g_tblInstance.find(hParent);	if (iter == g_tblInstance.end())		return false;	return true;}bool isInTable(HWND hParent, HWND hToolTips){	ListInstance * pList = NULL;	TableInstance::iterator iter = g_tblInstance.find(hParent);	if (iter == g_tblInstance.end()) // the parent window has not been register.	{		return false;	}	else // the parent windows has been registered we just get the parent wndproc from the other nodes.	{		pList = iter->second;		HWND hToolTips = *pList->begin();		ListInstance::const_iterator iterList = std::find(pList->begin(), pList->end(), hToolTips);		if (iterList == pList->end())			return false;	}	return true;}HWND getFirstToolTips(HWND hParent){	if (!isInTable(hParent))		return NULL;	ListInstance * pList = NULL;	TableInstance::iterator iter = g_tblInstance.find(hParent);	return *iter->second->begin();}CToolTips::CToolTips(HWND hParentWnd, HINSTANCE hInstance, bool MultiLine /*= false*/)	:m_szText(NULL),	m_multiText(NULL),	m_nWidth(0),	m_bMultiLine(MultiLine),	m_bActive(false),	m_bVisible(false),	m_hParent(hParentWnd),	m_hToolTips(NULL),	m_parentWndProc(NULL),	m_hInst(hInstance){	m_pos.x = 0;	m_pos.y = 0;	memset(&m_toolInfo, 0, sizeof(TOOLINFO));	if (_createToolTips())	{		_registerWndProc();		_addtoInstanceTable();	}}CToolTips::~CToolTips(){	_removeFromInstanceTable();	_unregisterWndProc();	if (m_hToolTips)	{		DestroyWindow(m_hToolTips);		m_hToolTips = NULL;	}	DELETEPV(m_szText);	DELETEPV(m_multiText);}bool CToolTips::_createToolTips(){	if (!m_hParent || !m_hInst)		return false;	// Create the Handle for the ToolTips control	m_hToolTips = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,		WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,		m_hParent, NULL, m_hInst, NULL);	return (m_hToolTips != NULL);}void CToolTips::initToolTips(){	if (!m_hToolTips || !m_hInst)		return;	SetWindowPos(m_hToolTips, HWND_TOPMOST, 0, 0, 0, 0,		SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);	//Init the TOOLINFO	m_toolInfo.cbSize = sizeof(TOOLINFO);	m_toolInfo.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE;	m_toolInfo.hwnd = m_hParent;	m_toolInfo.hinst = m_hInst;	m_toolInfo.lpszText = LPSTR_TEXTCALLBACK;	m_toolInfo.uId = (UINT_PTR)m_hParent;	// Associate the tooltip with the tool window.	SendMessage(m_hToolTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&m_toolInfo);	// Set the DelayTime of the ToolTips	SendMessage(m_hToolTips, TTM_SETDELAYTIME, (WPARAM)TTDT_AUTOPOP, (LPARAM)MAKELONG(30 * 1000, 0));	// By default, we just set the tooltips to inactive.	SendMessage(m_hToolTips, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&m_toolInfo);	m_bActive = true;}void CToolTips::_addtoInstanceTable(){	ListInstance * pList = NULL;	TableInstance::iterator iter = g_tblInstance.find(m_hParent);	if (iter == g_tblInstance.end()) // the parent window has not been register.	{		pList = new ListInstance;		pList->push_back(m_hToolTips);		g_tblInstance.insert(std::make_pair(m_hParent, pList));	}	else	{		pList = iter->second;		ListInstance::const_iterator iterSet = std::find(pList->begin(), pList->end(), m_hToolTips);		if (iterSet == pList->end())			pList->push_back(m_hToolTips);	}	}void CToolTips::_removeFromInstanceTable(){	TableInstance::iterator iter = g_tblInstance.find(m_hParent);	if (iter == g_tblInstance.end())		return;	ListInstance * pSet = iter->second;	pSet->remove(m_hToolTips);}void CToolTips::_registerWndProc(){	// bind the this pointer to the handle of the tooltips	SetWindowLongPtr(m_hToolTips, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)); 	// Register the windows proc for the tooltip dialog.	m_tooltipWndProc = (WNDPROC)SetWindowLongPtr(m_hToolTips,		GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&CToolTips::tooltipWndProc));	// Register the windows proc for the parents dialog.	if (!isInTable(m_hParent) && !m_parentWndProc)	{		m_parentWndProc = (WNDPROC)SetWindowLongPtr(m_hParent,			GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&CToolTips::parentWndProc));	}	else	{		HWND hToolTips;		if (!(hToolTips = getFirstToolTips(m_hParent)))			return;		LONG_PTR user_data = GetWindowLongPtr(hToolTips, GWLP_USERDATA);		CToolTips *pToolTips = reinterpret_cast<CToolTips*>(user_data);		m_parentWndProc = pToolTips->getParentWndProc();	}}void CToolTips::_unregisterWndProc(){	// if it is the last element relative to the parent dialog just unregister the wndproc.	TableInstance::iterator iter = g_tblInstance.find(m_hParent);	if (iter != g_tblInstance.end() && m_parentWndProc != NULL)	{		ListInstance *pSet = iter->second;		if (pSet->size() == 0)// it is the empty set.		{			(WNDPROC)SetWindowLongPtr(m_hParent,				GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_parentWndProc));			g_tblInstance.erase(iter);			DELETEP(pSet);		}		m_parentWndProc = NULL;	}	// unregister the window procedure and restore to the default procedure.	if (m_tooltipWndProc)	{		SetWindowLongPtr(m_hToolTips,			GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_tooltipWndProc));		m_tooltipWndProc = NULL;	}	// unregister the this pointer to the hwnd GWL_USERDATA 	SetWindowLongPtr(m_hToolTips, GWLP_USERDATA, NULL);}LRESULT CALLBACK CToolTips::tooltipWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam){	LONG_PTR user_data = GetWindowLongPtr(hWnd, GWLP_USERDATA);	CToolTips *this_window = reinterpret_cast<CToolTips*>(user_data);	if (!this_window || !this_window->m_tooltipWndProc)		return DefWindowProc(hWnd, Msg, wParam, lParam);	static bool g_TrackingMouse = false;	switch (Msg)	{	case WM_MOUSELEAVE:		g_TrackingMouse = false;		return DefWindowProc(hWnd, Msg, wParam, lParam);		break;	case WM_MOUSEMOVE:		if (!g_TrackingMouse)		{			TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT) };			tme.hwndTrack = hWnd;			tme.dwFlags = TME_LEAVE;			TrackMouseEvent(&tme);			this_window->setUpdate();			g_TrackingMouse = true;		}		break;	}	return CallWindowProc(this_window->m_tooltipWndProc, hWnd, Msg, wParam, lParam);}// hook for the parent window procedureLRESULT CALLBACK CToolTips::parentWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam){	LONG_PTR user_data = GetWindowLongPtr(getFirstToolTips(hWnd), GWLP_USERDATA);	CToolTips *this_window = reinterpret_cast<CToolTips*>(user_data);	if (!this_window || !this_window->getParentWndProc())		return DefWindowProcW(hWnd, Msg, wParam, lParam);	switch (Msg)	{	case WM_NOTIFY:		{			switch (((LPNMHDR)lParam)->code)			{			case TTN_GETDISPINFO: /*TTN_NEEDTEXT:*/				LPNMTTDISPINFO pInfo = (LPNMTTDISPINFO)lParam;				SendMessage(pInfo->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, this_window->m_nWidth);				lstrcpyn(pInfo->szText, this_window->m_multiText, ARRAYSIZE(pInfo->szText));				break;			}		}		break;	case WM_MOVE:		{			TableInstance::iterator iter = g_tblInstance.find(hWnd);			if (iter == g_tblInstance.end())				break;			ListInstance *pList = iter->second;			ListInstance::iterator  iterlist;			for (iterlist = pList->begin(); iterlist != pList->end(); ++iterlist)			{				LONG_PTR user_data = GetWindowLongPtr((HWND)(*iterlist), GWLP_USERDATA);				CToolTips *tooltips_window = reinterpret_cast<CToolTips*>(user_data);				if (!tooltips_window)					continue;				tooltips_window->setPosition(tooltips_window->m_pos);			}				}		break;	case WM_ACTIVATE:	{		TableInstance::iterator iter = g_tblInstance.find(hWnd);		if (iter == g_tblInstance.end())			break;		ListInstance *pList = iter->second;		ListInstance::iterator  iterlist;		for (iterlist = pList->begin(); iterlist != pList->end(); ++iterlist)		{			LONG_PTR user_data = GetWindowLongPtr((HWND)(*iterlist), GWLP_USERDATA);			CToolTips *tooltips_window = reinterpret_cast<CToolTips*>(user_data);			if (!tooltips_window)				continue;			if (LOWORD(wParam) == WA_INACTIVE)			{				tooltips_window->setActive(false);			}			else			{				tooltips_window->setActive(true);			}		}	}	}	return CallWindowProc(this_window->m_parentWndProc, hWnd, Msg, wParam, lParam);}void CToolTips::setText(LPCTSTR szText){	DELETEPV(m_szText);	m_szText = new TCHAR[lstrlen(szText) + 1];	lstrcpy(m_szText, szText);}void CToolTips::setMultiLineText(LPCTSTR szText, const LONG nWidth){	DELETEPV(m_multiText);	m_multiText = new TCHAR[lstrlen(szText) + 1];	lstrcpy(m_multiText, szText);	m_nWidth = nWidth;	if (m_bActive)	{		setUpdate();	}}void CToolTips::setPosition(const POINT& pt){	m_pos.x = pt.x;	m_pos.y = pt.y;	POINT newPt = { m_pos.x, m_pos.y};	ClientToScreen(m_hParent, &newPt);	SendMessage(m_hToolTips, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(newPt.x, newPt.y));}void CToolTips::setActive(bool bActive){	m_bActive = bActive;	if (m_bActive && m_bVisible)	{		SendMessage(m_hToolTips, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&m_toolInfo);		setPosition(m_pos);	}	else	{		SendMessage(m_hToolTips, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&m_toolInfo);	}	}void CToolTips::setVisible(bool bVisible){	m_bVisible = bVisible;	if (m_bVisible && m_bActive)	{		SendMessage(m_hToolTips, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&m_toolInfo);		setPosition(m_pos);	}	else	{		SendMessage(m_hToolTips, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&m_toolInfo);	}}void CToolTips::setUpdate(){	SendMessage(m_hToolTips, TTM_UPDATE, 0, 0);}








main.cpp 测试代码





#include <windows.h>#include <windowsx.h>#include <commctrl.h>#include <stdio.h>#include <assert.h>#include "resource.h"#include "CToolTips.h"#pragma comment(lib, "comctl32.lib")#define MULTILINE_TEXT	TEXT("This\nis a very long text string that must be broken into several lines.")#define MULTILINE_TEXT_TIME	TEXT("This\nis a very long text string that must be broken into several lines. %d")LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM);LRESULT CALLBACK MyDlgProc1(HWND, UINT, WPARAM, LPARAM);HINSTANCE g_hInst;CToolTips *g_pToolTips = NULL;CToolTips *g_pToolTips1 = NULL;int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, int){	g_hInst = hInstance;	INITCOMMONCONTROLSEX cx = { sizeof(INITCOMMONCONTROLSEX), ICC_BAR_CLASSES };	BOOL ret = InitCommonControlsEx(&cx);	return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MyDlgProc1);}void TEST_Constructor(HWND hDlg){	g_pToolTips = new CToolTips(hDlg, g_hInst, true);	assert(g_pToolTips != NULL);	g_pToolTips1 = new CToolTips(hDlg, g_hInst, true);}void TEST_Destructor(){	if (g_pToolTips)	{		delete g_pToolTips;		g_pToolTips = NULL;	}	if (g_pToolTips1)	{		delete g_pToolTips1;		g_pToolTips1 = NULL;	}}void TEST_MultilineToolTips(){	if (!g_pToolTips)		return;	g_pToolTips->setMultiLineText(MULTILINE_TEXT, 150);	g_pToolTips->initToolTips();	POINT pt = { 50, 50 };	g_pToolTips->setPosition(pt);	g_pToolTips->setVisible(true);	if (!g_pToolTips1)		return;	g_pToolTips1->setMultiLineText(MULTILINE_TEXT, 150);	g_pToolTips1->initToolTips();	POINT pt1 = { 100, 50 };	g_pToolTips1->setPosition(pt1);	g_pToolTips1->setVisible(true);}void TEST_StartDynamicUpdate(HWND HDlg){	SetTimer(HDlg, 1, 5000, NULL);}void TEST_DynamicUpdateToolTips(HWND hDlg){	TCHAR buf[255] = { 0 };	wsprintf(buf, MULTILINE_TEXT_TIME, GetCurrentTime());	//	//	static int i = 0;	if (i % 2 == 0)		g_pToolTips1->setMultiLineText(buf, 150);	else		g_pToolTips->setMultiLineText(buf, 150);	i++;}void TEST_KillDynamicUpdate(HWND hDlg){	KillTimer(hDlg, 1);}LRESULT CALLBACK MyDlgProc1(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){	static CToolTips * pToolTips;	switch (uMsg)	{	case WM_INITDIALOG:		TEST_Constructor(hDlg);		TEST_MultilineToolTips();		TEST_StartDynamicUpdate(hDlg);		break;	case WM_TIMER:		TEST_DynamicUpdateToolTips(hDlg);		break;	case WM_CLOSE:		TEST_KillDynamicUpdate(hDlg);		TEST_Destructor();		EndDialog(hDlg, FALSE);		break;	}	return FALSE;}








运行结果如下


创建了两个多行气泡的ToolTips,  定时器每间隔5秒会更新气泡的内容,气泡的位置会随着窗口被拖动而自行 调整, 当窗口处于非激活状态时候气泡自动隐藏。


单鼠标指向某一个气泡的时候,该气泡默认会显示在窗口最顶端。







上一篇:Windows消息大全
下一篇:《Windows程序设计》读书笔二十一 动态链接库

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2025年03月20日 14时21分23秒