Tracking tooltip in CScrollView?

746 Views Asked by At

In a standard C++/MFC MDI doc/view project, I want to implement a tracking tooltip in the view (the tabbed view windows which generally occupy most of the main frame window). So, in class MyAppView, I have a member CToolTipCtrl tooltip. Function MyAppView::OnInitialUpdate() contains the initialization

BOOL ok0 = tooltip.Create(this, TTS_ALWAYSTIP);
CRect clientRect; GetClientRect(&clientRect);
BOOL ok2 = tooltip.AddTool(this, LPSTR_TEXTCALLBACK, &clientRect, 1234/*tool ID*/);
tooltip.Activate(TRUE);

to make the entire client area of the view be the "tool". The message map contains an entry

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)

and the function OnNeedToolTipText is defined as

BOOL MyAppView::OnNeedToolTipText(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
{
    UNREFERENCED_PARAMETER(id);

    NMTTDISPINFO *pTTT = (NMTTDISPINFO *)pNMHDR;
    UINT_PTR nID = pNMHDR->idFrom;
    BOOL bRet = FALSE;
    if(nID == 1234)
    {
        // Come here when text is needed for tracking tooltip
    }
    if(pTTT->uFlags & TTF_IDISHWND)
    {
        // idFrom is actually the HWND of the tool
        nID = ::GetDlgCtrlID((HWND)nID);
        if(nID)
        {
            _stprintf_s(pTTT->szText, sizeof(pTTT->szText) / sizeof(TCHAR),
              _T("Control ID = %d"), nID);
            pTTT->hinst = AfxGetResourceHandle();
            bRet = TRUE;
        }
    }

    *pResult = 0;

    return bRet;
}

What happens is that only placing the mouse on the menu items (File, Edit, View, Window, Help) causes the code to enter OnNeedToolTipText, with an ID of 0-5. Moving the mouse into the client area (the view) does nothing.

How can I get the tooltip to appear in the client area of the view only?

Visual Studio 2017; C++; 64-bit Windows 7

4

There are 4 best solutions below

0
On

In order to solve the problem you need to do the following:

BOOL CMyAppView::PreTranslateMessage(MSG* pMsg)
{
    switch (pMsg->message)
    {
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
    case WM_LBUTTONDOWN:
    case WM_RBUTTONDOWN:
    case WM_MBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_RBUTTONUP:
    case WM_MBUTTONUP:
    case WM_MOUSEMOVE:
        if (m_pToolTip->GetSafeHwnd () != NULL)
        {
            m_pToolTip->RelayEvent(pMsg);
        }
        break;
    }

    return CScrollView::PreTranslateMessage(pMsg);
}
4
On

So I went back to see what I could be missing. I wrote this stuff over 10 years ago. I had also overridden a CWnd member

virtual INT_PTR OnToolHitTest( CPoint point, TOOLINFO* pTI ) const;

With:

INT_PTR HERichView::OnToolHitTest( CPoint point, TOOLINFO* pTI ) const
{
    pTI->hwnd = m_hWnd;
    pTI->uId = point.x + ( point.y << 16 );
    CRect rect;
    GetClientRect( rect );
    pTI->rect= rect;
    pTI->lpszText= LPSTR_TEXTCALLBACK;
    return pTI->uId;
}

And I checked, it won't work without this. So your:

ON_NOTIFY_EX( TTN_NEEDTEXT, 0, OnToolTip )

Should get called if you add the above. And only EnableToolTips( ); Should be needed.

0
On

If you want a tracking tooltip in a view, these are the steps to follow:

Create tooltip and add the tool.

void CToolTipDemoView::OnInitialUpdate()
{
    // ...

    m_toolTip.Create(this, TTS_ALWAYSTIP | TTS_NOANIMATE);
    m_toolTip.AddTool(this, _T("Doesn't matter"));
}

Handle WM_MOUSEMOVE message. First, call _TrackMouseEvent in order to further receive WM_MOUSELEAVE and activate the tooltip. Second, update the tooltip text, and show it at mouse pointer coordinates.

void CToolTipDemoView::OnMouseMove(UINT nFlags, CPoint point)
{
    if (!m_bTrackingMouseLeave)
    {
        TRACKMOUSEEVENT tme = { 0 };
        tme.cbSize = sizeof(TRACKMOUSEEVENT);
        tme.dwFlags = TME_LEAVE;
        tme.hwndTrack = m_hWnd;
        ::_TrackMouseEvent(&tme);

        m_toolTip.Activate(TRUE);
        m_bTrackingMouseLeave = TRUE;
    }

    if (m_pointLastMousePos != point)
    {
        CString strText;
        strText.Format(_T("x = %d y = %d"), point.x, point.y);
        m_toolTip.UpdateTipText(strText, this);
        m_toolTip.Popup();
        m_pointLastMousePos = point;
    }

    CScrollView::OnMouseMove(nFlags, point);
}

Handle WM_MOUSELEAVE and deactivate the tooltip.

void CCToolTipDemoView::OnMouseLeave()
{
    m_bTrackingMouseLeave = FALSE;

    // mouse pointer leaves the window so deactivate the tooltip
    m_toolTip.Activate(FALSE);

    CScrollView::OnMouseLeave();
}

Notes:

  • there is no more necessary to handle TTN_NEEDTEXT.
  • also, there is no more necessary to override PreTranslateMessage
0
On

I have not succeeded in getting the tracking tooltip to work within MFC. The closest I have come is

In message map: ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)

In OnInitialUpdate: BOOL ok1 = EnableTrackingToolTips(TRUE);

In override of virtual function OnToolHitTest:

    pTI->hwnd = m_hWnd;
    pTI->uId = (UINT_PTR)m_hWnd;
    pTI->uFlags = TTF_IDISHWND | TTF_ALWAYSTIP | TTF_TRACK | TTF_NOTBUTTON | TTF_ABSOLUTE | TTF_SUBCLASS;
    pTI->lpszText = LPSTR_TEXTCALLBACK;
    return pTI->uId;

In OnNeedToolTipText:

NMTTDISPINFO *pTTT = (NMTTDISPINFO *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if(pTTT->uFlags & TTF_IDISHWND)
{
    // idFrom is actually the HWND of the tool
    nID = ::GetDlgCtrlID((HWND)nID);
    if(nID)
    {
        CURSORINFO ci; ci.cbSize = sizeof(CURSORINFO); // get something interesting to display
        GetCursorInfo(&ci);
        _stprintf_s(pTTT->szText, sizeof(pTTT->szText) / sizeof(TCHAR),
            _T("Control ID = %lld at (%d, %d)"), nID, ci.ptScreenPos.x, ci.ptScreenPos.y);
        pTTT->hinst = AfxGetResourceHandle();
        bRet = TRUE;
    }
}
*pResult = 0;
return bRet;

This produces the following peculiar behavior. When I start the app and move the mouse cursor into the client area of the CScrollView, a tooltip appears right next to the cursor.

If I move the mouse carefully (smoothly) the tooltip tracks properly. After a while, though, it disappears, and no further mouse motions, including leaving the CScrollView window and returning, make it re-appear.

I think what is happening is that when the mouse cursor moves over the tooltip window, the tooltip is turned off, permanently. This disappearance does not seem to be time-related (e g, due to auto-pop); if the mouse is left untouched, the tooltip remains indefinitely.