该功能的实现来源于codeproject上面的一个例子《Tooltips for Menu Item and popup menuitem》,其实例子讲的步骤很简单,不过我不打算简单的翻译一下,林语堂先生不是说:“只用一样东西,不明白它的道理,实在不高明”。
实现该功能的核心在于作者自己建立的一个MenuToolTip类,182行的代码实现了菜单提示的功能,下面我就将其一一解释开来,在每行代码的后面有具体的解释。
#ifndef _MENU_TOOLTIP //条件编译语句,判断是否定义了_MENU_TOOLTI宏
#define _MENU_TOOLTIP
//以下定义一些常量标识符
#ifndef TTS_NOANIMATE
#define TTS_NOANIMATE 0x10
#define TTS_NOFADE 0x20
#define TTS_BALLOON 0x40
#define TTS_CLOSE 0x80
#define TTM_SETTITLEA (WM_USER + 32) // wParam = TTI_*, lParam = char* szTitle
#define TTM_SETTITLEW (WM_USER + 33) // wParam = TTI_*, lParam = wchar* szTitle
#ifdef UNICODE
#define TTM_SETTITLE TTM_SETTITLEW
#else
#define TTM_SETTITLE TTM_SETTITLEA
#endif
#endif
class CMenuToolTip
{
public:
CMenuToolTip():m_hToolTip(0), m_hParent(0) {}
// 创建与菜单项相关的提示
void Create(HWND hParent, LPCTSTR sczTipText,
HINSTANCE hInstance = NULL,
DWORD dwStyle = 0,
LPCTSTR sczTipTitle = NULL);
//触发WM_MENUSELECT消息时调用此函数
void OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSubMenu);
// 显示或隐藏提示的函数
void ShowToolTip(BOOL bShow)
{
TOOLINFO ti;//TOOLINFO是一个存放控件提示信息的结构
//以下初始化该结构中的一些变量
ti.cbSize = sizeof(TOOLINFO);//设定结构的大小
ti.uFlags = TTF_IDISHWND;//指出uid成员是窗口的句柄
ti.hwnd = m_hParent;//包含提示的窗口的句柄
ti.uId = (UINT)m_hParent;//应用程序定义的标识符
//发送出现提示的消息
::SendMessage(m_hToolTip,TTM_TRACKACTIVATE,(WPARAM)bShow,(LPARAM)&ti);
}
// 设置提示出现的位置
void SetToolTipPosition(HMENU hMenu, UINT nItemID)
{
RECT rt = {0,0,0,0};
// find Item Rectangle and position
//根据菜单项的数量完成以下循环
for(int nItem = 0; nItem < ::GetMenuItemCount(hMenu); nItem++) {
UINT cmd = ::GetMenuItemID(hMenu, nItem);//将当前菜单项的标识符存放到一个变量中
//如果当前菜单项是选定的菜单项,则获取菜单项的区域
if(cmd == nItemID) {
::GetMenuItemRect(m_hParent, hMenu, nItem, &rt);
}
}
//发送消息以设定显示提示的位置
::SendMessage(m_hToolTip, TTM_TRACKPOSITION, 0,
(LPARAM)MAKELPARAM(rt.right, rt.bottom + 2));
//将提示显示在最顶层,否则的话提示箭头会出现在菜单下面,试试看:->
::SetWindowPos(m_hToolTip, HWND_TOPMOST ,0,0,0,0, SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOMOVE);
}
// 更新提示信息
void UpdateToolTipText(LPCTSTR szBuff, HINSTANCE hInstance = 0)
{
TOOLINFO ti;
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND;
ti.hwnd = m_hParent;
ti.uId = (UINT)m_hParent;
ti.hinst = hInstance;
ti.lpszText = const_cast<LPTSTR>(szBuff);//更新提示文本的内容
//发送更新的消息
::SendMessage(m_hToolTip,TTM_UPDATETIPTEXT,(WPARAM)0,(LPARAM)&ti);
}
//重载句柄操作符
operator HWND()
{
return m_hToolTip;
}
private:
HWND m_hToolTip;
HWND m_hParent;
TCHAR m_szDefault[_MAX_PATH] ;//存放缺省提示的串
HINSTANCE m_hInstance;
};
inline//以下内联函数创建提示控件
void CMenuToolTip::Create(HWND hParent, LPCTSTR sczTipText, HINSTANCE hInstance,
DWORD dwStyle, LPCTSTR sczTipTitle)
{
INITCOMMONCONTROLSEX icex;//在公用控件的动态链接库中注册公用控件类
TOOLINFO ti;
// Load the ToolTip class from the DLL.
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_BAR_CLASSES;
//如果注册失败,则返回
if(!InitCommonControlsEx(&icex))
return;
m_hParent = hParent;
m_hInstance = hInstance;
// 创建提示控件
m_hToolTip = ::CreateWindow(TOOLTIPS_CLASS, TEXT(""),
WS_POPUP| dwStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, (HMENU)NULL, hInstance,
NULL);
// 设置控件的一些初始参数
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT | TTF_TRACK | TTF_ABSOLUTE;//TTF_CENTERTIP
ti.hwnd = hParent;
ti.uId = (UINT)hParent;
ti.hinst = hInstance;
ti.lpszText = const_cast<LPTSTR>(sczTipText);
if(sczTipText != LPSTR_TEXTCALLBACK) {
//以下设定缺省提示文本
if(sczTipText) {//如果当前菜单存在提示文本,则将其存放到m_szDefault
_tcscpy(m_szDefault, sczTipText);
}
else {//如果不存在提示文本,则显示以下字符串
_tcscpy(m_szDefault, _T("No Text associated"));
}
}
ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
//把提示添加到控件中
::SendMessage(m_hToolTip,TTM_ADDTOOL,0,(LPARAM)&ti);
::SendMessage(m_hToolTip, TTM_SETMAXTIPWIDTH, 0, 300);
if(sczTipTitle) {
::SendMessage(m_hToolTip, TTM_SETTITLE, 1, (LPARAM)sczTipTitle);
}
}
inline //以下内联函数设定在选定菜单时所做的操作
void CMenuToolTip::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSubMenu)
{
if(nFlags & MF_POPUP
|| (nFlags == 0xFFFF && hSubMenu == NULL)) { // 菜单关闭时不出现提示
ShowToolTip(FALSE);
}
if(!(nFlags & MF_POPUP)) {
// 设置提示的位置
SetToolTipPosition(hSubMenu, nItemID);
// 更新提示的文本
TCHAR szBuff[256];//定义一个存放提示文本的字符数组
szBuff[0] = 0;
//将指定的提示文本存放到预先设定的缓冲区中
int nRet = ::LoadString(m_hInstance, nItemID, szBuff, 256);
#if 0
for(int i = 0; i < nRet; i++)
{
if(szBuff[i] == _T('/n'))
{
szBuff[i] = 0;
break;
}
}
#endif
//以下部分主要用于更新提示文本,否则文本是固定的
if(szBuff[0] != 0) {
UpdateToolTipText(szBuff);
}
else {
UpdateToolTipText(m_szDefault);
}
ShowToolTip(TRUE);//设定需要显示提示
}
}
#endif _MENU_TOOLTIP
上面的注释写的比较详细了,您可以在相应的地方做修改,以更改提示的位置和内容,将上面的代码存为一个名为menutooltip.h的头文件,将其加入到您的工程当中,然后在需要处理提示信息的类的初始函数(如对话框的OnInitDialog函数或者是类的构造函数)中添加如下语句:
myMenuToolTip.Create(m_hWnd, _T("Sample Menu Text"), AfxGetResourceHandle()
,TTS_NOPREFIX | TTS_BALLOON
, _T("My Best Tool Tip Title")
);
同时在相应的类中创建OnMenuSelect函数如下:
void CMenuItemToolTipDlg::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSubMenu)
{
myMenuToolTip.OnMenuSelect(nItemID, nFlags, hSubMenu);
CWnd::OnMenuSelect(nItemID, nFlags, hSubMenu);
}
同时不要忘了加入上述头文件,并定义一个提示信息类的变量
#include "MenuToolTip.h"
CMenuToolTip myMenuToolTip;
最后在消息循环中加入ON_WM_MENUSELECT()
一切ok了。
Ps:在这里显示的提示信息都是您在定义菜单资源时设定的,也就是在定义一个菜单项时,右键选定属性,在general中有一个prompt文本框,用于设定提示文本,如果文本过长,可以在其中加上“/r/n”进行换行。