Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

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

Posted on 1998-04-23
12
Medium Priority
?
459 Views
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.
0
Comment
Question by:sdillman
[X]
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
12 Comments
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1301728
Yes
0
 
LVL 10

Expert Comment

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

0
 
LVL 10

Expert Comment

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

BOOL CMyView::PreTranslateMessage(MSG* pMsg) {
  m_ToolTip.RelayEvent(pMsg);
  return QIDView::PreTranslateMessage(pMsg);
}

0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 10

Expert Comment

by:RONSLOW
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);

0
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1301732
Next add the following to your class

in the message map add a line for

ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT,0,0xFFFF,OnToolTipNeedText)

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)) {
    TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
    // 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).

0
 

Author Comment

by:sdillman
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.
0
 

Author Comment

by:sdillman
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.
0
 
LVL 10

Expert Comment

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

Expert Comment

by:RONSLOW
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.

0
 

Author Comment

by:sdillman
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.
0
 
LVL 10

Accepted Solution

by:
RONSLOW earned 300 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.

0
 
LVL 10

Expert Comment

by:RONSLOW
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.
      m_ToolTip.Activate(FALSE);        
    }
    // if there is a new item
    if (pItem) {
      m_ToolTip.Activate(TRUE);
      m_pToolTipItem = pItem;
    }
  }
  CView::OnMouseMove(nFlage,point);
}

Sorry about the confusion.

0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

In this post we will learn different types of Android Layout and some basics of an Android App.
In this article we will learn how to backup a VMware farm using Nakivo Backup & Replication. In this tutorial we will install the software on a Windows 2012 R2 Server.
This tutorial will walk an individual through locating and launching the BEUtility application to properly change the service account username and\or password in situation where it may be necessary or where the password has been inadvertently change…
This tutorial will walk an individual through configuring a drive on a Windows Server 2008 to perform shadow copies in order to quickly recover deleted files and folders. Click on Start and then select Computer to view the available drives on the se…

618 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