如何重新定义默认工具提示行为?

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;

【讨论】:

以上是关于如何重新定义默认工具提示行为?的主要内容,如果未能解决你的问题,请参考以下文章

如何将自定义 css 工具提示添加到 extjs 列标题?

如何使用默认的组织模式电子表格自动求和行为?

如何使用工具提示格式化程序并仍然显示图表颜色(就像默认情况下一样)?

防止反应按钮组件中的默认按钮行为(页面重新加载)

如何自定义Visual Studio快捷方式

css 这定义了如何沿当前行的横轴布置弹性项目的默认行为。把它想象为正当理由