Solved

Tooltip Question

Posted on 1998-05-19
5
460 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

Efficient way to get backups off site to Azure

This user guide provides instructions on how to deploy and configure both a StoneFly Scale Out NAS Enterprise Cloud Drive virtual machine and Veeam Cloud Connect in the Microsoft Azure Cloud.

Question has a verified solution.

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

Unlike C#, C++ doesn't have native support for sealing classes (so they cannot be sub-classed). At the cost of a virtual base class pointer it is possible to implement a pseudo sealing mechanism The trick is to virtually inherit from a base class…
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 goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

803 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