Showing Tool Tips for an Area of a Window, Not for a Control

Posted on 1998-04-23
Last Modified: 2013-11-20
I have a custom control that is implemented as a derived class from CWnd.  I would like to provide tool tips for various rectangular areas within the client area of the custom control.

I am familiar with implementing tool tips for controls within a dialog by overriding the virtual OnToolHitTest() function and by providing a OnToolTipNotify() message handler for the TTN_NEEDTEXT notification.  When providing tool tips for controls, you fill in the TOOLINFO structure in OnToolHitTest() by specifying TTF_IDISHWND in the uFlags member.  If you fill in the appropriate window handles in the hwnd and uID members, then your OnToolTipNotify() function gets called correctly.

In my case, however, I am not dealing with a tool tip on a control, but rather on an area of the window.  If you read the documentation on TOOLINFO, you can see that there is a member of the structure that you can use to specify the rectangular region for the tool.  The docs also say, however, that this member is ignored if you have TTF_IDISHWND in uFlags.  Since I am not providing a tool tip for a control, I don't want to include TTF_IDISHWND in the uFlags member.  For some reason, however, when I remove this from the flags, my OnToolTipNotify() function never gets called.

Has anyone ever implemented tool tips on a rectangular area of the client window rather than on a control?  Please tell me how to do this and still be able to provide the tool-tip text on demand in a OnToolTipNotify() function.
Question by:sdillman
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 3
LVL 10

Expert Comment

ID: 1301728
LVL 10

Expert Comment

ID: 1301729
I'll post some code for you ASAP .. .takes a little while to cut, trim and paste

LVL 10

Expert Comment

ID: 1301730
First step .. in your view class...

BOOL CMyView::PreTranslateMessage(MSG* pMsg) {
  return QIDView::PreTranslateMessage(pMsg);

NFR key for Veeam Backup for Microsoft Office 365

Veeam is happy to provide a free NFR license (for 1 year, up to 10 users). This license allows for the non‑production use of Veeam Backup for Microsoft Office 365 in your home lab without any feature limitations.

LVL 10

Expert Comment

ID: 1301731
Next you need some members added to your view class

  CToolTipCtrl m_ToolTip;
  CMyItem* m_pToolTipItem;

where I've assumed (as in my code) that your document class has a list of items that it displays on the view, and this is a pointer to the one we wnat to get a tip on.

If this is not the case, then you need some other indicator as to what part of your window the mouse is over.

In you message map you need a line for

afx_msg BOOL OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult);

LVL 10

Expert Comment

ID: 1301732
Next add the following to your class

in the message map add a line for


and also add the function

BOOL CMyView::OnToolTipNeedText(UINT /*id*/, NMHDR * pNMHDR, LRESULT * pResult) {
  *pResult = 0;
  CPoint pointClient;
  ::GetCursorPos(&pointClient);      // get cursor pos in screen coords
  ScreenToClient(pointClient);      // convert to client coords
  CRect rectClient;
  GetClientRect(rectclient);            // get client rectangle
  // Make certain that the cursor is in the client rect, because
  // the mainframe also wants these messages to provide tooltips
  // for the toolbar.
  if (rectClient.PtInRect(pointClient)) {
    // find the item at the cursor position
    // using my GetHitItem function
    m_pToolTipItem = GetHitItem(pointWorld);
    // if there is an item here
    if (m_pToolTipItem) {
      // Adjust the text by filling in TOOLTIPTEXT
      CString strTip = m_pToolTipItem->WhoAmI();
      if (strTip.GetLength() >= sizeof(pTTT->szText)) {
        strTip = strTip.Left(sizeof(pTTT->szText)-4) + "...";
      ::strcpy(pTTT->szText, strTip);
    } else {
      pTTT->szText[0] = '\0';
    return true;
  return false;

You'll need to write your own GetHitItem (or equivalent) that returns a pointer to (or some indication of) the area over which the mouse is sitting.

In my cas, GetHitItem asks the doc to iterate thru the items in its list and find out which one's bounding rect contains the point, then returns a poitner to the item found (or NULL if nothing found).


Author Comment

ID: 1301733
I changed my code over to use a CToolTipCtrl object in my CWnd-derived class.  I called CToolTipCtrl::Create() once my window was created, and I called CToolTipCtrl::AddTool() to add a tool that corresponds to a rectangle within my window.  I specified LPSTR_TEXTCALLBACK so that it should send me the TTN_NEEDTEXT message.  I added the TTN_NEEDTEXT entry to the message map and I added the OnToolTipNeedText member function to my CWnd-derived class.  My OnToolTipNeedText function never gets called.  It should get called when I leave the mouse cursor over the rectangle within my window that I specified in the AddTool() call.  This is the same behavior that I saw using the other method.  My window never receives the TTN_NEEDTEXT message.

Things to Note:
I am not using a CView-derived class as you are.  My class is a custom control derived from CWnd.
My window that I am trying to get tool tips for exists as a custom control on a CDialogBar.

Any ideas are greatly appreciated.

Author Comment

ID: 1301734
On a hunch, I tried overriding the OnNotify() function in my CWnd-derived class.  Sure enough, I receive TTN_NEEDTEXT notification calls to this function when using both my original method with OnToolHitTest(), and when using the CToolTipCtrl method described by RONSLOW.  The problem is apparently in the message-map handling of the TTN_NEEDTEXT message.

For now, I am going to go ahead and process the message directly in OnNotify().  If anyone can figure out why the message handler is not called when putting the ON_NOTIFY_EX entry in the message map, I would still appreciate the information.  I did find some references on the web about an error in VC++ 5.0 SP2 that affected TTN_NEEDTEXT notifications.  I'm not sure if this is related.
LVL 10

Expert Comment

ID: 1301735
I TOLD you how to make it work .. why did you not follow what I suggested?
LVL 10

Expert Comment

ID: 1301736
you say "I changed my code over to use a CToolTipCtrl object in my CWnd-derived class.  I called CToolTipCtrl::Create() once my window was created, and I called CToolTipCtrl::AddTool() to add a tool that corresponds to a rectangle within my window.  I specified LPSTR_TEXTCALLBACK so that it should send me the TTN_NEEDTEXT message.  I added the TTN_NEEDTEXT entry to the message map and I added the OnToolTipNeedText member function to my CWnd-derived class."

That was not what I said to do.  There is not NEED for all that.

You SHOULDN'T put a ON_NOTIFY_EX entry .. you put in a ON_NOTIFY_EX_RANGE entry otherwise it won't get called (because the id's don't match)

The code I suggested to you is from working code of my own .. it should work fine.


Author Comment

ID: 1301737
I tried to follow EXACTLY what you suggested, but it did not seem complete (and it didn't work).  And yes, I did use ON_NOTIFY_EX_RANGE in my code.  After putting in the exact code that you suggested and running into some problems, I started trying to fix it.  For one thing, without calling CToolTipCtrl::Create(), the m_ToolTip.RelayEvent(pMsg) call in PreTranslateMessage will fail because the CWnd for the object does not exist.  I didn't see how you could have a CToolTipCtrl object and never create the window for it or add tools to it, so I tried those things.  I got your code to the point where I receive notification messages if I override OnNotify() (just as I do with my code) but the ON_NOTIFY_EX_RANGE entry in the message map would never route the messages to my message handler (just as it does not with my code).  There are several differences in what we are working with that might cause problems.  As I mentioned before, I am not working with a document/view architecture but rather with a CWnd-derived custom control on a CDialogBar.  I have come across many problems in the past with messages getting routed to controls on a CDialogBar, and this may be yet another such problem.

I will review your original code suggestions and see if I missed anything in my implementation, but I have a deadline coming up fast and I have the code working at this point (just not in the clean manner I had hoped for).  I thank you again for all of your help.  Hopefully I will have some time soon to try a simplified version of my situation and see if I can figure out what is going on.
LVL 10

Accepted Solution

RONSLOW earned 150 total points
ID: 1301738
My humble apologies .. just found the code I forgot to post

In the OnInitialUpdate for the view:

  if (! m_ToolTip.GetSafeHwnd() && ! ::IsWindow(m_ToolTip.GetSafeHwnd())) {
    if (m_ToolTip.Create(this, TTS_ALWAYSTIP) && m_ToolTip.AddTool(this)) {
      m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, /*SHRT_MAX*/160);
      m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_AUTOPOP, /*SHRT_MAX*/5000);
      m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_INITIAL, 500);
      m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_RESHOW, 500);
    } else {
      TRACE("Error in creating ToolTip");

Hope that clears things up

NOTE that this tool tip apepars after you hover for a short time and then automatically disappears.  It also limits its width so that it will go multi-line if a longer tip is required.

LVL 10

Expert Comment

ID: 1301739
Just found another bit ... this is what happens when one tries to extract relevant bits from large chunks of wroking code...

void CMyView::OnMouseMove(UINT nFlags, CPoint point) {
  // if we have a tool tip
  if (::IsWindow(m_ToolTip.m_hWnd)) {
    // is there an item under the mouse
    CMyItem* pItem = GetHitItem(point);
    // if no item or item change, kill old tooltip
    if (! pItem || pItem != m_pToolTipItem) {
      // Use Activate() to hide the tooltip.
    // if there is a new item
    if (pItem) {
      m_pToolTipItem = pItem;

Sorry about the confusion.


Featured Post

Free eBook: Backup on AWS

Everything you need to know about backup and disaster recovery with AWS, for FREE!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

This article is an update and follow-up of my previous article:   Storage 101: common concepts in the IT enterprise storage This time, I expand on more frequently used storage concepts.
A Bare Metal Image backup allows for the restore of an entire system to a similar or dissimilar hardware. They are highly useful for migrations and disaster recovery. Bare Metal Image backups support Full and Incremental backups. Differential backup…
This tutorial will show how to configure a single USB drive with a separate folder for each day of the week. This will allow each of the backups to be kept separate preventing the previous day’s backup from being overwritten. The USB drive must be s…
This tutorial will walk an individual through setting the global and backup job media overwrite and protection periods in Backup Exec 2012. Log onto the Backup Exec Central Administration Server. Examine the services. If all or most of them are stop…

730 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question