Parent-Independent ToolTip Support for Static (or any) Controls

I have come across many problems related to handling ToolTips in controls without parent involvement. Many samples show how to use ToolTips in a dialog by creating CToolTipCtrl, enabling it, and adding tools. Although it helps, it may turn out that achieving additional functionality proves to be difficult; for example: displaying different text for the same control or displaying information based on area in a window (rectangle). Implementing ToolTips proves to be more difficult for static controls created by inserting the control in a dialog template as well as those that are dynamically created. That is a main reason I have concentrated on the Static control.

All MFC CWnd-derived classes have automatic ToolTips built in. It is just a matter of enabling the tools and properly handling ToolTip messages. This approach may be used on any control, and any child window for that matter, for which we need to handle ToolTips independently of the parent window.

I have written a class that demonstrates ToolTip handling, derived from CStatic, called CStaticTTPSupport; the name is a little long, but serves the purpose. You should treat this class as a boilerplate and change code handling text assignment logic as well as determine a hit value, tailoring it to your needs.

A CWnd implementation of PreTranslateMessage filters messages that are needed for built-in ToolTips by calling FilterToolTipMessage. This in turn calls a virtual override of OnToolHitTest. A default implementation returns -1 if the window does not have children or if the window has ID_STATIC (-1). To change this behavior, the function must be overridden.

int CStaticTTPSupport::OnToolHitTest(CPoint point, TOOLINFO *pTI) const
{
   pTI->hwnd = m_hWnd;
   pTI->uFlags = 0;                       // we need to differ tools
                                          // by ID, not window handle
   pTI->lpszText = LPSTR_TEXTCALLBACK;    // tell ToolTips to send
                                          // TTN_NEEDTEXT

   for(int iIndx = 0; iIndx < _ZONE_COUNT * 2; iIndx++)
   {
      if(m_rectZone[iIndx].PtInRect(point))
      {
         // point is in this rectangle.
         pTI->rect = m_rectZone[iIndx];
         // TTN_NEEDTEXT Message will use this ID to fill NMHDR structure
         pTI->uId = iIndx;
         break;
      }
   }

   return iIndx;    // return index of rectangle
}

This function sets the TOOLINFO structure that is used by FilterToolTipMessage. The hwnd member is set to the control's handle. It is important because this indicates the window that will receive TTN_NEEDTEXT notification because the lpszText member is initialized with LPSTR_TEXTCALLBACK. See code comments next to the member initialization.

In a nutshell: If the function returns value greater that -1, FilterToolTipMessage will determine the course of action that may show a new popup if needed and kill the old tool. If the hit's value is -1, the ToolTip window is deactivated.

After setting tool information, you need a handler for TTN_NEEDTEXT notification.

  • The notification message handler will always handle TTN_NEEDTEXT notification messages sent exclusively to a control; the control does not have any children. You do not need to indicate whether or not the message was handled; therefore, the ON_NOTIFY_EX macro is not needed.
  • The ToolTip ID is always 0; therefore, the ON_NOTIFY_RANGE macro is not needed.

That is why I used the ON_NOTIFY macro to handle TTN_NEEDTEXT in this particular scenario. In other cases, it may be necessary using the macros that I mentioned above. Mapping macros look as follows:

ON_NOTIFY(TTN_NEEDTEXTW, 0, OnToolTipNotify)
ON_NOTIFY(TTN_NEEDTEXTA, 0, OnToolTipNotify)

As you can see, I have mapped two versions of the TTN_NEEDTEXT notification code: ANSI and UNICODE. That may look as though it is unnecessary because the application that is built is not UNICODE. The real reason for handling both is not because the application may be built as UNICODE; it is a ToolTip control that, in newer versions of ComCtl32, have controls built for UNICODE and you may receive UNICODE. For the same reason, the TTN_NEEDTEXT handler handles ANSI and UNICODE versions.

In a sample class that you may have tuned to your needs, I have decided to assign different ToolTip text based on the cursor position over different areas of the window. The client area was divided into 16 rectangles in two rows and eight columns. ToolTip text is set depending on the cursor position over a given rectangle. This is determined in OnToolHitTest, where the hit value is assigned to a TOOLINFO structure.

The sample dialog uses three static controls: the first from a template, subclasses using class wizard; the second that is created and placed in dialog; and the third from a template subclassed without a wizard. The last one demonstrates how to subclass an already existing control by using its window handle, without regard to the control's ID value (IDC_STATIC is used.)

Additional Information

TTN_NEEDTEXT notification was superseded with TTN_GETDISPINFO and TOOLTIPTEXT superseded with NMTTDISPINFO. For benefit of all programmers who use an older SDK and/or VS versions, I decided to use older definitions. This does not break anything because TTN_NEEDTEXT is defined as TTN_GETDISPINFO and TOOLTIPTEXT structure is defined as NMTTDISPINFO for newer releases.



About the Author

John Z. Czopowik VC++ MVP

Microsoft VC++ MVP

Downloads

Comments

  • Nice

    Posted by kirants on 02/28/2005 12:01pm

    Well written and very useful piece of information explained neatly.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Contact center infrastructure vendors continue to integrate their multichannel capabilities to develop ''omnichannel'' solutions, while enhancing their cloud delivery capabilities. Gartner Magic Quadrant reports evaluate contact center solution providers based on completeness of vision and ability to execute. As you are looking to modernize your existing on premises contact center solution or considering cloud as an alternative, check out these Gartner Reports for strengths and cautions to consider when …

  • One of the country's marquee events, Microsoft's annual event 'Ignite' lights up various major cities, with an average attendance of over 30,000. In 2016, the Microsoft Learning group had several mandates to incorporate different campaigns and programs into the event, under a defined budget, with limited resources, and of course, under tight timelines. Read this case study to learn how Microsoft was able to use cloud-based tools to aggregate, organize, display, and distribute content quickly, and at scale, …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date