如何重新定义默认工具提示行为?
Posted
技术标签:
【中文标题】如何重新定义默认工具提示行为?【英文标题】:How to redefine default tooltip behaviour? 【发布时间】:2019-10-21 12:47:43 【问题描述】:我创建了样本来检查tooltip
控件。这是一个有点杂乱的程序,它创建窗口、按钮并注册按钮工具提示,消息为qwerty
。次要日志已添加到每个 window
/button
/tooltip
窗口。
#include <windows.h>
#include <Windowsx.h>
#include <CommCtrl.h>
#include <string>
#include <map>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static HWND hwndTip = 0;
static HWND tool = 0;
WNDPROC old_tooltip_proc = 0;
WNDPROC old_button_proc = 0;
std::string GetLastErrorAsString()
//Get the error message, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0)
return std::string(); //No error message has been recorded
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
//Free the buffer.
LocalFree(messageBuffer);
return message;
void log_msg(std::string prefix, UINT msg)
std::map<UINT, std::string> table
WM_NCHITTEST, "WM_NCHITTEST",
TTM_WINDOWFROMPOINT, "TTM_WINDOWFROMPOINT",
WM_SETCURSOR, "WM_SETCURSOR",
WM_PAINT, "WM_PAINT",
WM_NCPAINT, "WM_NCPAINT",
WM_ERASEBKGND, "WM_ERASEBKGND",
WM_SHOWWINDOW, "WM_SHOWWINDOW",
WM_ACTIVATEAPP, "WM_ACTIVATEAPP",
WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING",
WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED",
WM_GETTEXT, "WM_GETTEXT",
WM_MOUSELEAVE , "WM_MOUSELEAVE",
WM_GETTEXTLENGTH, "WM_GETTEXTLENGTH",
WM_NCCALCSIZE, "WM_NCCALCSIZE",
WM_TIMER, "WM_TIMER",
WM_MOVE, "WM_MOVE",
WM_MOUSEMOVE, "WM_MOUSEMOVE",
WM_LBUTTONDOWN, "WM_LBUTTONDOWN",
TTM_RELAYEVENT, "TTM_RELAYEVENT",
SB_SETTEXTA, "SB_SETTEXTA"
;
if (table.find(msg) == table.end())
OutputDebugString((prefix + " " + std::to_string(msg) + "\n").c_str());
return;
OutputDebugString((prefix + " " + table.at(msg) + "\n").c_str());
LRESULT CALLBACK tooltip_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
log_msg("TOOLTIP", message);
return CallWindowProc(old_tooltip_proc, hwnd, message, wParam, lParam);
LRESULT CALLBACK button_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
log_msg("BUTTON", message);
return CallWindowProc(old_button_proc, hwnd, message, wParam, lParam);
void createToolTip(HINSTANCE hInstance, HWND parent_window)
hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
parent_window, NULL, hInstance,
NULL);
old_tooltip_proc = (WNDPROC)SetWindowLongPtr(hwndTip, GWLP_WNDPROC, (LONG_PTR)tooltip_proc);
if (!hwndTip)
MessageBox(parent_window, "CreateWindowEx TOOLTIPS_CLASS failed", "ERROR", MB_OK);
return;
TOOLINFO get_tool_info(HWND tool)
TOOLINFO g_toolItem = 0 ;
g_toolItem.cbSize = sizeof(g_toolItem);
g_toolItem.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
g_toolItem.hwnd = GetParent(tool);
g_toolItem.uId = (UINT_PTR)tool;
g_toolItem.hinst = NULL;
g_toolItem.lpszText = LPSTR_TEXTCALLBACK;
return g_toolItem;
void register_tool(HWND _tool)
std::string f("qwerty");
TOOLINFO toolinfo = get_tool_info(_tool);
toolinfo.lpszText = const_cast<char*>(f.c_str());
if (SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolinfo) == FALSE)
MessageBox(toolinfo.hwnd, "TTM_ADDTOOL failed", "ERROR", MB_OK);
return;
tool = _tool;
void unregister_tool(HWND tool)
TOOLINFO toolinfo = get_tool_info(tool);
SendMessage(hwndTip, TTM_DELTOOL, 0, (LPARAM)&toolinfo);
HWND createButton(HWND parent_window)
auto handle = CreateWindow(TEXT("button"), TEXT("Hellooooooooooooooooooooooooooooo"),
WS_VISIBLE | WS_CHILD,
10, 10, 800, 250,
parent_window, NULL, NULL, NULL);
old_button_proc = (WNDPROC)SetWindowLongPtr(handle, GWLP_WNDPROC, (LONG_PTR)button_proc);
return handle;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
INITCOMMONCONTROLSEX ic;
ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
ic.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&ic);
static TCHAR szAppName[] = TEXT("ToolTipApplication");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
hwnd = CreateWindow(szAppName, // window class name
TEXT("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
HWND button = createButton(hwnd);
createToolTip(hInstance, ::GetDesktopWindow());
register_tool(button);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
TranslateMessage(&msg);
DispatchMessage(&msg);
return msg.wParam;
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
log_msg("window", message);
return DefWindowProc(hwnd, message, wParam, lParam);
工具提示工作正常,但我想替换默认行为。如果您将鼠标悬停在按钮上,将显示工具提示。但是当我在工具提示处快速移动光标时,工具提示将消失。我想禁用这种消失。系统托盘中的工具提示类似于此行为。如果您将鼠标悬停在工具提示上,它不会同时消失。类似的东西:
附:我试图重新定义对TTM_RELAYEVENT
消息的处理,但没有给出任何结果。
【问题讨论】:
【参考方案1】:添加TTF_TRANSPARENT
TTF_TRANSPARENT:使工具提示控件转发鼠标事件 消息到父窗口。这仅限于鼠标事件 出现在工具提示窗口的范围内。
代码:
TOOLINFO get_tool_info(HWND tool)
TOOLINFO g_toolItem = 0 ;
g_toolItem.cbSize = sizeof(g_toolItem);
g_toolItem.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_TRANSPARENT;
g_toolItem.hwnd = GetParent(tool);
g_toolItem.uId = (UINT_PTR)tool;
g_toolItem.hinst = NULL;
g_toolItem.lpszText = LPSTR_TEXTCALLBACK;
return g_toolItem;
【讨论】:
以上是关于如何重新定义默认工具提示行为?的主要内容,如果未能解决你的问题,请参考以下文章