最近在MFC中使用ToolTip时有诸多疑惑,查阅了不少资料,仔细研究了MSDN文档,在此记录使用方法和注意事项,与大家共勉。
首先,思考一下,如果是我们自己来实现ToolTip(工具提示)的功能,要如何做呢?最自然的想法是:当我们把鼠标移动到希望提示的窗口时弹出一个提示窗口,我们把鼠标移出希望提示的窗口时关闭提示窗口。OK,微软也不会比我们聪明多少,它的实现基本思想就是这样的。
需要注意的是微软为了保证使用的方便,使用了设计模式中的观察者模式,这样使用方便,但是却造成了理解上的不易。
简单来说,观察者模式就是设计有观察者和被观察对象,当被观察对象触发某些特定的动作时,会通知指定的观察者采取相关动作。这里观察者和被观察者要事先绑定并指定被观察对象如何向观察者发出通知,一般采用回调函数或消息通知的形式,这是Windows系统,当然是后一种了。
结合这里ToolTip的实现,观察者就是ToolTip(工具提示)窗口,被观察者就是希望提示的窗口(Tool窗口),当鼠标移动到希望提示的窗口(Tool窗口)上这一事件触发后就会通知ToolTip(工具提示)窗口弹出,或者当鼠标移出希望提示的窗口(Tool窗口)上这一事件触发后就会通知ToolTip(工具提示)窗口隐藏。
那么理论上,一般ToolTip的使用步骤如下:
1.创建Tooltip窗口,一般一个ToolTip窗口可用于多个Tool窗口的提示。
2.添加Tooltip窗口所观察Tool窗口
3.所观察Tool窗口产生鼠标移入和移出事件时通知Tooltip窗口显示和隐藏
基本上微软的实现和我们理论上描述的差不多,我们要实际使用ToolTip主要是搞懂消息传递时的参数意义。
实际使用步骤如下:
这个过程传递TOOLINFO类型参数来区别不同的Tool窗口和对应的提示文字
TOOLINFO的定义如下
typedef struct tagTOOLINFO{
UINT cbSize;
UINT uFlags;
HWND hwnd;
UINT_PTR uId;
RECT rect;
HINSTANCE hinst;
LPTSTR lpszText;
#if (_WIN32_IE >= 0x0300)
LPARAM lParam;
#endif
#if (_WIN32_WINNT >= 0x0501)
void *lpReserved;
#endif
} TOOLINFO, NEAR *PTOOLINFO, *LPTOOLINFO;
部分参数定义如下
[1].cbSize
指的是TOOLINFO的大小,这里一定要注意,许多有问题的地方就源于这里。主要问题是下面这一句的定义
#if (_WIN32_WINNT >= 0x0501)
void *lpReserved;
#endif
在一些低版本的系统中使用高版本的编译器,如在Windows 2000中使用VS 2005,这时候编译器默认的版本大于0x0501,这个时候会有void *lpReserved定义,但是实际的Windows 2000中的
comctl32.dll版本偏低,并没有定义相关字段,当实际调用时传递给dll中的TOOLINFO参数中cbSize偏大,dll检测到这一条件后添加Tool窗口就失败了。
解决方法很简单,加载对应的dll或者减少cbSize。
在高版本的编译器中,定义对应低版本操作系统的版本号_WIN32_WINNT 就可以减少cbSize了
提供高版本的dll并在高版本的编译器中使用清单文件即可加载对应的DLL版本
当然,如果你不是在低版本的系统中使用高版本的编译器中使用的话就不需要考虑这个问题了。最常见的问题就是在XP中使用VS2005、2008系列编译器引起的问题,这种情况下最简单的就是链接正确的6.0版本 DLL如下:
#if defined _M_IX86
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_IA64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#elif defined _M_X64
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#else
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
[2].uFlags
指明一些配置参数,见具体使用说明
以下两个参数指明希望显示工具提示的窗口或控件,只需要选择一种情况
[3].rect
如果希望鼠标移动到某一个窗口的一个指定区域时显示提示,这个rect就是指定区域,这个时候hwnd就是这个窗口句柄。这个时候uID只是一个自定义标识,用来区别不同的区域。
[4].uId
如果希望标移动到某一个控件时显示提示:uId就是这个控件的ID号,这时候需要指明包含这个控件的窗口句柄hwnd;如果指定uFlags参数包含TTF_IDISHWND,这时候uId就是这个控件的句柄,这时候不需要指明hwnd。
以下参数指明希望显示的工具提示文字
[5].lpszText
指明工具提示的显示文字:当定义了hInstance时,lpszText是对应的字符串资源ID号;如果没有定义hInstance,lpszText指向自定义的字符串buffer。如果指明lpszText参数为LPSTR_TEXTCALLBACK,创建ToolTip控件时会发送给hwnd指定的窗口一个TTN_GETDISPINFO的通知消息让用户响应这个消息以获得显示的工具提示字符串。
//创建提示窗口
hwndTip = CreateWindowEx( WS_EX_TOPMOST,
TOOLTIPS_CLASS,
NULL,
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
hDlg,
NULL,
hInstance,
NULL
);
/**
*功能: 为hwnd指定窗口区域rect添加工具提示文字szTipText
*参数: hwndTip -- 用于显示提示文字的ToolTip窗口
* hwnd、rect、uId --要显示提示文字的窗口、区域和自定义的ID
* szTipText --待显示的工具提示文字
*返回: 无
*其他: 2014/05/30 By Jim Wen Ver1.0
**/
void AddRectTool(HWND hwndTip,
HWND hwnd, RECT rect, UINT uid,
LPCTSTR szTipText)
{
TOOLINFO tti;
//设置提示窗口的信息
memset(&tti, 0, sizeof(TOOLINFO));
tti.cbSize = sizeof(TOOLINFO);
tti.uFlags = TTF_SUBCLASS;
tti.hwnd = hwnd;
tti.rect = rect;
tti.uId = uid;
tti.lpszText = szTipText;
//新增一个提示
SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &tti);
}
/**
*功能: 为指定控件添加工具提示文字
*参数: hwndTip -- 用于显示提示文字的ToolTip窗口
* hwnd、uId --控件的父窗口、控件ID
* hInstance、szStringRes --应用程序句柄和工具提示文字字符串资源ID
*返回: 无
*其他: 2014/05/30 By Jim Wen Ver1.0
**/
void AddWindowTool1 (HWND hwndTip,
HWND hwnd, UINT uid,
HINSTANCE hInstance, LPCTSTR szStringRes)
{
TOOLINFO tti;
//设置提示窗口的信息
memset(&tti, 0, sizeof(TOOLINFO));
tti.cbSize = sizeof(TOOLINFO);
tti.uFlags = TTF_IDISHWND | TTF_SUBCLASS ;
tti.uId = (UINT_PTR)GetDlgItem(hwnd, uid);
tti.hinst = hInstance;
tti.lpszText = szStringRes;
//新增一个提示
SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &tti);
}
/**
*功能: 为指定控件添加工具提示文字,这里的文字通过给hwnd发送消息来获得
*参数: hwndTip -- 用于显示提示文字的ToolTip窗口
* hwnd、uId --控件的父窗口、控件ID
*返回: 无
*其他: 2014/05/30 By Jim Wen Ver1.0
**/
void AddWindowTool2 (HWND hwndTip,
HWND hwnd, UINT uid)
{
TOOLINFO tti;
//设置提示窗口的信息
memset(&tti, 0, sizeof(TOOLINFO));
tti.cbSize = sizeof(TOOLINFO);
tti.uFlags = TTF_IDISHWND | TTF_SUBCLASS ;
tti.hwnd = hwnd;
tti.uId = (UINT_PTR)GetDlgItem(hwnd, uid);
tti.lpszText = LPSTR_TEXTCALLBACK;
//新增一个提示
SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &tti);
}
/**
*功能: 为指定控件添加工具提示文字,这个工具提示是随鼠标移动的,这个不能定义TTF_SUBCLASS,必须自己传递鼠标消息
* 在WM_MOUSE中通过TTM_TRACKPOSITION来确认工具提示文字位置
* 可以通过TTM_TRACKACTIVATE来确认显示和隐藏
* 不能和其他的提示共用一个ToolTip提示窗口
*参数: hwndTip -- 用于显示提示文字的ToolTip窗口
* hwnd、uId --控件的父窗口、控件ID
* szTipText --待显示的工具提示文字
*返回: 无
*其他: 2014/05/30 By Jim Wen Ver1.0
**/
BOOL g_bIsVisible=FALSE;
void AddWindowTool3 ( HWND hwndTip,
HWND hwnd, UINT uid,
LPCTSTR szTipText)
{
TOOLINFO tti;
//设置提示窗口的信息
memset(&tti, 0, sizeof(TOOLINFO));
tti.cbSize = sizeof(TOOLINFO);
tti.uFlags = TTF_IDISHWND | TTF_TRACK ;
tti.uId = (UINT_PTR)GetDlgItem(hwnd, uid);
tti.lpszText = szTipText;
//新增一个提示
SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &tti);
//显示工具提示
SendMessage(hwndTip, TTM_TRACKACTIVATE,(WPARAM)TRUE,(LPARAM)&tti);
g_bIsVisible = TRUE;
}
/**
*功能: 为指定控件添加多行工具提示文字,这里的文字必须通过给hwnd发送消息来获得
*参数: hwndTip -- 用于显示提示文字的ToolTip窗口
* hwnd、uId --控件的父窗口、控件ID
*返回: 无
*其他: 2014/05/30 By Jim Wen Ver1.0
**/
void AddWindowTool4 (HWND hwndTip,
HWND hwnd, UINT uid,
LPCTSTR szTipText)
{
TOOLINFO tti;
//设置提示窗口的信息
memset(&tti, 0, sizeof(TOOLINFO));
tti.cbSize = sizeof(TOOLINFO);
tti.uFlags = TTF_CENTERTIP | TTF_IDISHWND | TTF_SUBCLASS ;
tti.hwnd = hwnd;
tti.uId = (UINT_PTR)GetDlgItem(hwnd, uid);
tti.lpszText = LPSTR_TEXTCALLBACK;
//新增一个提示
SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &tti);
}