Solved

Tooltip Question

Posted on 1998-05-19
5
459 Views
Last Modified: 2011-10-03

Title: "VC++ question

   From: englm                    Date: Friday, May 15 1998 - 10:51AM PDT
 
   You recently answered a question for me on how to determine if my mouse is
   over a particular control - my next step is to get a tooltip to work for this control.

   I f somewhere in my view code I declare:
       
               CToolTipCtrl  m_mytooltip;

   then under  //do whatever below:

   BOOL CTestDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
         {
          if (pWnd->GetSafeHwnd() == m_btnCancel.GetSafeHwnd())
          {
   ====>// do whatever
           
              m_mytooltip.Create(this);
     
        }
          return CDialog::OnSetCursor(pWnd, nHitTest, message);
         }
   What else is needed once I create an instance of the tooltip?
0
Comment
Question by:linda101698
  • 2
  • 2
5 Comments
 
LVL 10

Expert Comment

by:RONSLOW
ID: 1164323
In your OnInitDialog include the line...
    EnableToolTips(true);
which will automatically create a tooltip control for you (MFC does this)

In your message map, add
    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT,0,0xFFFF,OnToolTipNotify)
which handles requests for the tooltip text for all controls (hence the 0,0xFFFF above)

In your dialog, you need the OnToolTipNotify handler like this...

BOOL CMyDialog::OnToolTipNotify( UINT /*id*/, NMHDR * pNMHDR, LRESULT * /*pResult*/ ) {
    static CString text;
    TOOLTIPTEXT *pTTT = reinterpret_cast<TOOLTIPTEXT*>(pNMHDR);
    UINT nID =pNMHDR->idFrom;
    if (pTTT->uFlags & TTF_IDISHWND) {
        pTTT->hinst = NULL;
        pTTT->lpszText = NULL;
        pTTT->szText[0] = '\0';
        // idFrom is actually the HWND of the tool
        nID = ::GetDlgCtrlID(reinterpret_cast<HWND>(nID));
        if(nID) {
            text = ToolTipString(nID);
            if (! text.IsEmpty()) {
                pTTT->lpszText = const_cast<LPTSTR>((LPCTSTR)text);
                pTTT->hinst = NULL;
                return true;
            }
        }
    }
    return(false);
}

I then define the (virtual) member function ToolTipString like this...

CString CMyDialog::ToolTipString(UINT id) {
    return ::ToolTipString(id);
}

[so one can override it for a given dialog is required]  Where ::ToolTipString is a global function like this (note: handles special ID codes)

CString ToolTipString(UINT id) {
    switch (id) {
    case IDOK: return "Apply any change and close the dialog";
    case IDCANCEL: return "Ignore any changes and close the dialog";
    // other special cases here, like IDABORT etc
    default:
        {
             CString tiptext;
             if (! tiptext.LoadString(id)) tiptext.Empty();
             return tiptext;
        }
    }
}

NOTE: The hard-coded strings above _should_ be loaded from the string table resource to be nice.   The resaon why they are handled specially is because the predefined ID numbers for IDOK and IDCANCEL would probably clash with other string resources in your app.

I've split the code up here into a couple of functions (rather than one) not because it is strictly necessary, but so as to make the code more flexible and easier to use.

NOTE 2: These is a bug in (some versions on) VC 5 regarding tooltips.. so you need the following to fix it...

int CMyDialog::OnToolHitTest(CPoint point, TOOLINFO* pTI) const {
    UINT cbSizeWas;
    if (pTI) {
        cbSizeWas = pTI->cbSize;
        pTI->cbSize = sizeof(TOOLINFO);
    }
    int ret = CDialog::OnToolHitTest(point,pTI);
    if (pTI) {
        pTI->cbSize = cbSizeWas;
    }
    return ret;
}

Now make sure you define string resources with the same ID's as your controls.

0
 
LVL 3

Expert Comment

by:tma050898
ID: 1164324
RONSLOW's code is perfect for situations where you want "automatic" support for tooltips. All you have to do is call a single function and add string resources for each control that want to have a tooltip.

However, I couldn't get it to work with static controls and I wanted a method that would allow me to specify the string id, (instead of the string id having to be the same as the control id). I know for our shop, keeping these ids in synch would be a major pain.


To use this class simply add a member variable of type CToolTip to the dialog, call the tool tip's Create function in the dialog's OnInitDialog function and call any of the three overloaded AddWindowTool functions for each control you wish to have a tooltip.

class CToolTip : public CToolTipCtrl
{
public:
 BOOL AddWindowTool(UINT uiControlId);
 BOOL AddWindowTool(UINT uiControlId, UINT uiStringId);
 BOOL AddWindowTool(UINT uiControlId, LPCTSTR pszText);

protected:
 BOOL AddRectTool(CWnd* pWnd, LPCTSTR pszText,
  LPCRECT lpRect, UINT nIDTool);
};

BOOL CToolTip::AddWindowTool(UINT uiControlId) // assumes control id = string id
{      
 return AddWindowTool(uiControlId, uiControlId);
}

BOOL CToolTip::AddWindowTool(UINT uiControlId, UINT uiStringId)
{      
 CString strText;
 VERIFY(strText.LoadString(uiStringId));
 return (AddWindowTool(uiControlId, strText));
}

BOOL CToolTip::AddWindowTool(UINT uiControlId, LPCTSTR pszText)
{      
 BOOL bSuccess = FALSE;

 CWnd* pParentWnd = GetOwner();
 ASSERT(pParentWnd); // did you call create first?
 if (pParentWnd)
 {
  CWnd* pWnd = pParentWnd->GetDlgItem(uiControlId);
  ASSERT(pWnd);
  if (pWnd)
  {
   TOOLINFO ti;
   ti.cbSize = sizeof (TOOLINFO);      
   ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
   ti.hwnd = pWnd->GetParent()->GetSafeHwnd();
   ti.uId = (UINT) pWnd->GetSafeHwnd();      
   ti.hinst = AfxGetInstanceHandle();
   ti.lpszText = (LPTSTR) pszText;      
   SendMessage( TTM_DELTOOL, 0, (LPARAM) &ti );
   bSuccess = (BOOL) SendMessage( TTM_ADDTOOL, 0, (LPARAM) &ti );
  }
 }

 return bSuccess;
}

BOOL CToolTip::AddRectTool(CWnd* pWnd, LPCTSTR pszText,LPCRECT lpRect, UINT nIDTool)
{      
TOOLINFO ti;      
ti.cbSize = sizeof (TOOLINFO);
ti.uFlags = TTF_SUBCLASS;      
ti.hwnd = pWnd->GetSafeHwnd();      
ti.uId = nIDTool;
ti.hinst = AfxGetInstanceHandle();      
ti.lpszText = (LPTSTR) pszText;
::CopyRect( &ti.rect, lpRect );
return (BOOL) SendMessage( TTM_ADDTOOL, 0, (LPARAM) &ti );
}

Then, in your dialog's OnInitDialog function you can use any of the three overloaded AddWindowTool functions like this. They all resolve to the same codepath, but give you a little extra flexibility.

BOOL CYourDialog::OnInitDialog()
{
 ...
 m_tooltip.Create(this);

 m_tooltip.AddWindowTool(IDOK, "OK"); // hard coded tooltip

 m_tooltip.AddWindowTool(IDOK); // string id = control id

 m_tooltip.AddWindowTool(IDOK, IDS_OK); // string id != control id
 
 ...
}

IMPORTANT!!!
As I wouldn't want other people taking credit for my work, so I want to mention that Ian (the only name I have) wrote this class. I simply added the 2 overloaded AddWindowTool functions so that string ids could be used. His email is ianm@iexplorers.com if you want to thank him for providing this code for everyone.

Tom
0
 
LVL 7

Author Comment

by:linda101698
ID: 1164325
One of you needs to post an answer so I can grade the question :-)

Thanks for the cooperation on this!
0
 
LVL 3

Accepted Solution

by:
tma050898 earned 0 total points
ID: 1164326
I am answering this question so that linda can close it. However, both comments below give very good examples (one by RONSLOW and one by me) as to how to implement tooltips for dialog controls.

Tom

0
 
LVL 7

Author Comment

by:linda101698
ID: 1164327
Thanks again for cooperating on this.

Linda

0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

895 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

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now