1

How can I set In-Place Tooltip for my longer text of control just like: enter image description here

I have already use this type of Tooltip by using ToolTip.Show Method

ToolTip ttpInplace = new ToolTip();
ttpInplace.Show(textbox1.Text, textbox1, 0, 0, 4000);

        private void FormToolPopup_MouseEnter(object sender, EventArgs e)
        {
            if (ttpCustomToolTip != null)
            {
                ttpCustomToolTip.Hide(textBox1);
            }
        }

        private void textBox1_MouseEnter(object sender, EventArgs e)
        {
            Size s = TextRenderer.MeasureText(textBox1.Text, textBox1.Font);
            if (s.Width > textBox1.Width)
            {
                ttpCustomToolTip.Show(textBox1.Text, textBox1, -4, 0);//, 5000);
            }
        }

But it flickers. I have read the article from http://msdn.microsoft.com/en-us/library/windows/desktop/hh298402(v=vs.85).aspx but I have problem to implement because it is in C++. Any one can help me?

10
  • Interrupts? What does this have to do with interrupts? What do you mean by "using interrupts"? Commented Feb 12, 2012 at 13:06
  • I mean sending tooltip messages. Sorry for that. Commented Feb 12, 2012 at 13:08
  • What are you using? Winforms? WPF? Something else? Commented Feb 12, 2012 at 13:12
  • Where exactly you show your tooltip ? If it's in the MouseMove event that could be the source of the flickering... Could you post a longer piece of code ? Commented Feb 12, 2012 at 13:19
  • @ digEmAll! I have put previous code. Please review. Commented Feb 12, 2012 at 13:27

2 Answers 2

6

Yes, it flickers because you display the tooltip at the same location as the mouse. So the tooltip gets an immediate mouse move message. Which makes the tip disappear. Which cause the MouseEnter event to fire again. Etcetera, flickorama.

One workaround is to capture the mouse so it cannot send a message to the tooltip:

   textBox1.Capture = true;
   ttpCustomToolTip.Show(textBox1.Text, textBox1, -4, 0);//, 5000);

The capture is automatically canceled when the user clicks the mouse. Might be good enough, you may have to set it back to false. Depends. Best thing to do is implement the MouseMove event and cancel both the tooltip and the capture when you see it moving outside of the control. You'll also have to do something to make the textbox usable, your tooltip is blocking access. At least one reason you don't often see this used. If editing is not intended then be sure to use a Label. Note its AutoEllipsis property.

Sign up to request clarification or add additional context in comments.

4 Comments

@ Hans Passant! It stopped flickering. Thanks a lot but what is the best way to hide tooltip while leaving from textbox1?
I explicitly talked about that in the last paragraph of the answer.
@ Hans Passant: If textBox1.Capture = true, then all the event have blocked until mouse clicked and there is no way to set textBox1.Capture false.
No, events are not blocked. In particular the textbox' MouseMove event will fire.
0

To eliminate in-place tooltip popup flickering problem, the most natural way is to enable "transparency" for your tooltip-window.

Raymond Chen's code demonstrates this. From: https://devblogs.microsoft.com/oldnewthing/20060626-11/?p=30743

It runs like this:

Theee image description

To enable transparency, you can choose either method.

[Method 1] When createing tooltip window using CreateWindowEx, pass WS_EX_TRANSPARENT window Ex-style.

[Method 2] For TTM_ADDTOOL operation, let TOOLINFO.uFlags include TTF_TRANSPARENT flag.

Here, "transparency" does not mean that the tooltip-window is partially visible(alpha-blending transparency effect), but means that mouse message over the tooltip surface is transparent, i.e., mouse messages hit the very window beneath the tooltip-windows(in Z-order), not disturbing tooltip-window itself. Hans Passant is right to say "it flickers because you display the tooltip at the same location as the mouse", but the solution he provides is really wacky.

If you take neither method, the in-place tooltip flickering occurs, easily verified by modifying Raymond's code.

It's sad that MSDN example page (https://learn.microsoft.com/en-us/windows/win32/controls/implement-in-place-tooltips) refers nothing to this "transparency" requirement. Sigh, this is just one example of MSDN's absent/vague information, and many of them(Win32 API) has not been updated for 15+ years.

Complete RaymondIPT.cpp below:

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <CommCtrl.h>
#include <tchar.h>
#include <assert.h>


#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")


HINSTANCE g_hinst;          /* This application's HINSTANCE */

// new >>>
HFONT g_hfTT;
HWND g_hwndTT;
RECT g_rcText;
LPCTSTR g_pszText = TEXT("Lorem ipsum dolor sit amet.");
const int c_xText = 50;
const int c_yText = 50;
// new <<<


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PTSTR szCmdLine, int nCmdShow)
{
    g_hinst = hInstance;

    InitCommonControls(); // WinXP requires this, to work with Visual-style manifest

    (void)hPrevInstance; (void)szCmdLine; 
    static TCHAR szAppName[] = TEXT ("RaymondTooltip") ;
    HWND         hwnd = NULL;
    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 (hInstance, MAKEINTRESOURCE(1)) ;
    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
    wndclass.lpszMenuName  = NULL ;
    wndclass.lpszClassName = szAppName ;

    RegisterClass (&wndclass);
     
    hwnd = CreateWindow (szAppName,    // window class name
        TEXT ("The RaymondTooltip Program"), // window caption
        WS_OVERLAPPEDWINDOW,           // window style
        CW_USEDEFAULT,   // initial x position
        CW_USEDEFAULT,   // initial y position
        400,             // initial x size
        200,             // initial y size
        NULL,            // parent window handle
        NULL,            // window menu handle
        hInstance,       // program instance handle
        NULL) ;          // creation parameters
     
    ShowWindow (hwnd, nCmdShow) ;
    UpdateWindow (hwnd) ;
    
    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }

    return (int)msg.wParam; // the value N told by PostQuitMessage(N);
}

BOOL Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
    g_hwndTT = CreateWindowEx(WS_EX_TRANSPARENT, 
        TOOLTIPS_CLASS, 
        NULL, // window title
        TTS_NOPREFIX | TTS_NOANIMATE | TTS_ALWAYSTIP,
        0, 0, 0, 0,
        hwnd, NULL, nullptr, NULL);
    if (!g_hwndTT)
        return FALSE;

    g_hfTT = GetStockFont(OEM_FIXED_FONT); //(ANSI_VAR_FONT);
    SetWindowFont(g_hwndTT, g_hfTT, FALSE);
    HDC hdc = GetDC(hwnd);
    HFONT hfPrev = SelectFont(hdc, g_hfTT);
    SIZE siz;
    GetTextExtentPoint(hdc, g_pszText, lstrlen(g_pszText), &siz);
    SetRect(&g_rcText, c_xText, c_yText,
        c_xText + siz.cx, c_yText + siz.cy);
    SelectFont(hdc, hfPrev);
    ReleaseDC(hwnd, hdc);

    TOOLINFO ti = { sizeof(ti) };
    ti.uFlags = TTF_SUBCLASS | TTF_TRANSPARENT;
    ti.hwnd = hwnd;
    ti.uId = 0;
    ti.lpszText = const_cast<LPTSTR>(g_pszText);
    ti.rect = g_rcText;

    BOOL succ = SendMessage(g_hwndTT, TTM_ADDTOOL, 0, (LPARAM)&ti);
    assert(succ);

    // Chj: Set tooltip delay-times.
    succ = SendMessage(g_hwndTT, TTM_SETDELAYTIME, TTDT_INITIAL, 10);
    succ = SendMessage(g_hwndTT, TTM_SETDELAYTIME, TTDT_AUTOPOP, 12000);

    return TRUE; // success, go on creation
}

void Cls_OnPaint(HWND hwnd)
{
    PAINTSTRUCT ps = {}, *pps = &ps;
//  RECT        rect ;
    HDC hdc = BeginPaint (hwnd, &ps) ;

    HFONT hfPrev = SelectFont(pps->hdc, g_hfTT);
    TextOut(pps->hdc, g_rcText.left, g_rcText.top,
        g_pszText, lstrlen(g_pszText));
    SelectFont(pps->hdc, hfPrev);

    EndPaint (hwnd, &ps) ;
}

void Cls_OnDestroy(HWND hwnd)
{
    PostQuitMessage(44);
}


LRESULT OnTooltipShow(HWND hwnd, NMHDR *pnm)
{
    TOOLINFO ti = {sizeof(ti)};
    ti.hwnd = hwnd;
    BOOL succ = SendMessage(pnm->hwndFrom, TTM_GETTOOLINFO, 0, (LPARAM)&ti);

    RECT rc = g_rcText;
    MapWindowRect(hwnd, NULL, &rc);
    SendMessage(pnm->hwndFrom, TTM_ADJUSTRECT, TRUE, (LPARAM)&rc);
    SetWindowPos(pnm->hwndFrom, 0, rc.left, rc.top, 0, 0,
        SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
    return TRUE; // suppress default positioning
}

LRESULT Cls_OnNotify(HWND hwnd, int idFrom, NMHDR *pnm)
{
    if (pnm->hwndFrom == g_hwndTT) {
        switch (pnm->code) {
        case TTN_SHOW:
            return OnTooltipShow(hwnd, pnm);
        }
    }
    return 0;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    
    switch (message)
    {{
        HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate);
        HANDLE_MSG(hwnd, WM_PAINT, Cls_OnPaint);
        HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnDestroy);

        HANDLE_MSG(hwnd, WM_NOTIFY, Cls_OnNotify);
    }}
    
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}

Compile it in VC2010+ using command line:

cl RaymondIPT.cpp /link user32.lib gdi32.lib comctl32.lib

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.