Solved

Tooltip Question

Posted on 1998-05-19
5
458 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Thanks again for cooperating on this.

Linda

0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

744 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