该功能的实现来源于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(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(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; } } # |