Click here to Skip to main content
Click here to Skip to main content

ListBox With ToolTip Support

By , 4 Jan 2002
 

Sample Image - CExListBoc.gif

Introduction

I've needed a simple listbox with tool tip support for my project, i couldn't find a listbox derived class that will do job. Salman A Khilji offers tool tip support in his article "List Box with ToolTips", but he his offering a multi-line tool tip which display's a pre-defined string as set in the dialog function. Also, the tips are always shown in the upper right corner.

In order to add tool tips support do the following:

  1. Add ExListBox.cpp and ExListBox.h to your project. 
  2. Add include "ExListBox.h" to the header file of your dialog class. 
  3. Add a list box control to your dialog. 
  4. Use the ClassWizard to add a member variable of type CListBox for the list box you just added.
  5. Manually change the member variable type to CExListBox in your header file of your dialog class. 

That's it, your list box will now display tool tip according to the item's text!

CExListBox - Explanation 

First we must enable the tool tip, so we add the following line to the PreSubclassWindow function:

void CExListBox::PreSubclassWindow() 
{
// TODO: Add your specialized code here and/or call the base class

CListBox::PreSubclassWindow();
EnableToolTips(TRUE);

}

Now , we need to override OnToolHitTest function in order  to provide the struct(s) for the ListBox.

int CExListBox::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
{
    int row;
    RECT cellrect; // cellrect - to hold the bounding rect
    BOOL tmp = FALSE;
    row = ItemFromPoint(point,tmp); //we call the ItemFromPoint function to determine the row,
    //note that in NT this function may fail use the ItemFromPointNT member function
    if ( row == -1 ) 
        return -1;

    //set up the TOOLINFO structure. GetItemRect(row,&cellrect);
    GetItemRect(row,&cellrect);
    pTI->rect = cellrect;
    pTI->hwnd = m_hWnd;
    pTI->uId = (UINT)((row)); //The ‘uId’ is assigned a value according to the row value.
    pTI->lpszText = LPSTR_TEXTCALLBACK;  //Send a TTN_NEEDTEXT messages 
    return pTI->uId;

}

As you can see , we first find out on which item the mouse points using the ItemFromPoint function . NOTE: in NT this function may fail, so use the ItemFromPointNT function (included in this class )

After we obtained the item pointed by mouse , we need to assign the item tool tip text , we assign to the TOOLINFO text member the LPSTR_TEXTCALLBACK which sends TTN_NEEDTEXT messages.

All we need to do now , is to supply handles for TTN_NEEDTEXT messages (wide and ansi ).

In the implementation file we add :

BEGIN_MESSAGE_MAP(CExListBox, CListBox)
//{{AFX_MSG_MAP(CExListBox)
// NOTE - the ClassWizard will add and remove mapping macros here.

//}}AFX_MSG_MAP
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()

And then add the OnToolTipText function :

BOOL CExListBox::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
// need to handle both ANSI and UNICODE versions of the message
    TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
    TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
    CString strTipText;
    UINT nID = pNMHDR->idFrom;  //list box item index 
    GetText( nID ,strTipText); //get item text 

//display item text as tool tip 
#ifndef _UNICODE
    if (pNMHDR->code == TTN_NEEDTEXTA)
    lstrcpyn(pTTTA->szText, strTipText, 80);
else
    _mbstowcsz(pTTTW->szText, strTipText, 80);
#else
    if (pNMHDR->code == TTN_NEEDTEXTA)
    _wcstombsz(pTTTA->szText, strTipText, 80);
    else
    lstrcpyn(pTTTW->szText, strTipText, 80);
    #endif
    *pResult = 0;

return TRUE; 
}

The source code

//
//#include "stdafx.h"

#include "ExListBox.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


/////////////////////////////////////////////////////////////////////////////
// CExListBox

CExListBox::CExListBox()
{
}

CExListBox::~CExListBox()
{
}


BEGIN_MESSAGE_MAP(CExListBox, CListBox)
    //{{AFX_MSG_MAP(CExListBox)
        // NOTE - the ClassWizard will add and remove mapping macros here.

    //}}AFX_MSG_MAP
    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CExListBox message handlers

void CExListBox::PreSubclassWindow() 
{
    // TODO: Add your specialized code here and/or call the base class
    

    CListBox::PreSubclassWindow();
    EnableToolTips(TRUE);

}

int CExListBox::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
{
    int row;
    RECT cellrect;   // cellrect        - to hold the bounding rect
    BOOL tmp = FALSE;
    row  = ItemFromPoint(point,tmp);  //we call the ItemFromPoint function to determine the row,
    //note that in NT this function may fail  use the ItemFromPointNT member function

    if ( row == -1 ) 
        return -1;

    //set up the TOOLINFO structure. GetItemRect(row,&cellrect);
    GetItemRect(row,&cellrect);
    pTI->rect = cellrect;
    pTI->hwnd = m_hWnd;
    pTI->uId = (UINT)((row));   //The ‘uId’ is assigned a value according to the row value.
    pTI->lpszText = LPSTR_TEXTCALLBACK;
    return pTI->uId;

}


//Define OnToolTipText(). This is the handler for the TTN_NEEDTEXT notification from 
//support ansi and unicode 
BOOL CExListBox::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
    // need to handle both ANSI and UNICODE versions of the message
    TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
    TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
    CString strTipText;
    UINT nID = pNMHDR->idFrom;

    
    GetText( nID ,strTipText);

#ifndef _UNICODE
    if (pNMHDR->code == TTN_NEEDTEXTA)
        lstrcpyn(pTTTA->szText, strTipText, 80);
    else
        _mbstowcsz(pTTTW->szText, strTipText, 80);
#else
    if (pNMHDR->code == TTN_NEEDTEXTA)
        _wcstombsz(pTTTA->szText, strTipText, 80);
    else
        lstrcpyn(pTTTW->szText, strTipText, 80);
#endif
    *pResult = 0;

    return TRUE;    
}




UINT CExListBox::ItemFromPoint2(CPoint pt, BOOL& bOutside) const

// CListBox::ItemFromPoint does not work on NT.

{
    int nFirstIndex, nLastIndex;
    //GetFirstAndLastIndex(nFirstIndex, nLastIndex);
    nFirstIndex = GetTopIndex();
    nLastIndex = nFirstIndex  + GetCount(); 


    
    bOutside = TRUE;
    
    CRect Rect;
    int nResult = -1;
    
    for (int i = nFirstIndex; nResult == -1 && i <= nLastIndex; i++)
    {
        if (GetItemRect(i, &Rect) != LB_ERR)
        {
            if (Rect.PtInRect(pt))
            {
                nResult  = i;
                bOutside = FALSE;
            }
        }
        
    }
    
    return nResult;
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

ran wainstein
Web Developer
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionFixes for no TTN_NEEDTEXT, no tip, tip under listbox in CComboBox listmemberGregSmith23 Jan '12 - 1:01 
I am using VS2010 and was trying to add tips to a drop down ComboBox list. I make my own CComboBox derived from CComboBox and then subclass the list box (use GetComboBoxInfo() to get the handle).
 
Using the method given here, you get the OnToolHitTest() called, but no TTN_NEEDTEXT. To get the message sent I needed to set TOOLINFO.uFlags = TTF_TRANSPARENT|TTF_ALWAYSTIP;
 
However, the next problem is that the tooltip now appears, but is under the drop down list (which is set to be a topmost window). You can find examples in MFC where CFrameWnd sets the tooltip to be the topmost window in the TTN_NEEDTEXT message, but this doesn't work (or at least does not for me). The workable solution I found is to add:
 
// Stop the ListBox control being on top of the tooltip.
::SetWindowPos(m_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
    SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE);
 
at the end of the TTN_NEEDTEXT handler. This sets the doropdown list to be on top of all non-topmost windows. This seems to work and not to mess up the drop down list.
GeneralCould provide the details of the license this code it shared under is it COPLmemberPSVN Roy13 Oct '10 - 7:18 
Please share the details of sharing this code
Generalproblem found when 2 listboxes on a dialog.memberMember 37416425 Feb '08 - 22:03 
I add 2 CExListBoxes on my dialog. Only one of them shows the tooltip...
QuestionRe: problem found when 2 listboxes on a dialog.mvpDavidCrow6 May '09 - 3:05 
Did you remember to call EnableToolTips(TRUE) in OnInitDialog()?
 

"Old age is like a bank account. You withdraw later in life what you have deposited along the way." - Unknown

"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons


GeneralSubclassed CListBox don't receive TTN_NEEDTEXTmemberk200v314 Nov '07 - 4:29 
I create a Combobox with subclassed DerivedCListBox
 
enable tool tip in DerivedCListBox
 
int DerivedCListBox::OnToolHitTest(CPoint point, TTTOOLINFOW *pTI) const;
{
CRect rc;
GetClientRect(&rc);
BOOL bOutside = FALSE;
if(rc.PtInRect(point))
{
UINT nItem = ItemFromPoint(point, bOutside);
GetItemRect(nItem, &rc);
 
pTI->hwnd = m_hWnd;
pTI->rect = rc;
pTI->uId = nItem;
pTI->lpszText = LPSTR_TEXTCALLBACK;
return nItem;
}
return -1;
}
 
BOOL DerivedCListBox::OnNeedText( UINT id, NMHDR * pTTTStruct, LRESULT * pResult )
{
...
}
map messages
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnNeedText);
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnNeedText);
 
When mouse moved over DerivedCListBox OnToolHitTest normally called
 
but function OnNeedText never called.
 
How can i catch TTN_NEEDTEXT in this case?
 

WinXP, VS2005
GeneralRe: Subclassed CListBox don't receive TTN_NEEDTEXTmemberMember 32291892 Sep '09 - 23:27 
Does anyone know the answer? i meet this problem too.
GeneralI add \r\n in tooltip text, but the tooltip still showed as a single line!memberTcpip20051 Nov '05 - 19:29 
How to solve this problem?
GeneralThank you for your work.memberDavid.Kelly26 Aug '05 - 10:30 
I was looking to add tool tips to a combo box and I had been searching code project, code guru, and planet-source-code.com for several days; add in the MSDN sites and I've gotten very little work done in the last three days until I ran across your article on the list box. An article I found at , started me looking at listbox articles because of the way MFC handles CComboBox messages. After downloading your demo I was able to add a CMapStringToString and a couple of lines of code to the control and display custom tool tips on the list. Thank you for your work
 
David Kelly
Novice Programmer and all around nice guy
QuestionHow to delay the tooltip text?memberdaveice21 Aug '05 - 20:31 
The tooltip is shown immediately when the mouse is over the item..
 
How can I delay it?
GeneralTooltip Flickeing in HTMLsussAnonymous28 Aug '04 - 0:30 
As the text of the title attribute exceeds a certain limit, flickering of the tooltip is observed. Is there a provision for dynamic repositioning and resetting the size of a tooltip
GeneralToolTip For ComboBoxmemberBitsAndBytes29 Jul '04 - 23:19 
Is it possible to give tool tip for various ComboItems in a dropdown combo box at the time of selection. i.e. When we click the combo, it shows the combo item in a dropdown and the comboitem gets highlighted as the mouse moves over the combo item. At the same time, is it possible to show a ToolTip for that ComboItem Confused | :confused: Confused | :confused: Confused | :confused:
 
Thanks in advance...
 
**************
Bits&Bytes !!!
__o
_-\<,_
(_)/ (_)

GeneralSelective tooltipsmemberandrew.truckle@atkinsglobal.com4 Mar '04 - 5:15 
What I am looking for is a listbox that only shows tooltips for lines of text LONGER than the width of the listbox (ie: you can't see all the text).
 
The listbox would be towards the right of the screen, so the tooltip would need to workout it's size and offset to the left in order to show up correctly. It should use the row position as the location to place the tooltip.
 
I hope this makes sense.
 
Could this listbox class be adapated to behave how I need?
 
Thanks.
 
Andrew
GeneralRe: Selective tooltipssussccl07526 Jul '04 - 12:14 
Andrew,
Did you get solution yet? I also tried to apply in-place tooltip(move tooltip window on top of row position) for each item in the list box. But can't get it work.
Could you please tell me resolution if you have a solution for it?
 
Thanks!
 
-------------------------------
CRect rc;
this->GetItemRect(nID,&rc);
this->ClientToScreen(&rc);
CWnd * pWnd = CWnd::FromHandle(pNMHDR->hwndFrom);
 
if( NULL != m_ToolTipCtrl.GetSafeHwnd() )
{

m_ToolTipCtrl.AdjustRect(&rc, TRUE);
 
m_ToolTipCtrl.SetWindowPos(pWnd, rc.left, rc.top, 0, 0,
SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
}
------------------------------------------
 
Thanks!
GeneralRe: Selective tooltipsmemberandrew.truckle@atkinsglobal.com26 Jul '04 - 19:52 
Send me your email address. i have sent you an email requesting it via codeproject.
GeneralRe: Selective tooltipssussccl07527 Jul '04 - 5:59 
Andrew,
Did you get solution yet? I also tried to apply in-place tooltip(move tooltip window on top of row position) for each item in the list box. But can't get it work.
Could you please tell me resolution if you have a solution for it?
 
Thanks!
 

 
Thanks!
GeneralRe: Selective tooltipsmemberandrew.truckle@atkinsglobal.com27 Jul '04 - 19:15 
I already asked you to send me your email address. I will then post you some code.
 
Andrew
GeneralRe: Selective tooltipsmemberChunyun29 Jul '04 - 8:18 
Andrew,
My email: chunyun_liu@yahoo.com
 
If you can post some sample code for handling in-place tooltips that will make my life better.
 
Thanks!
 
chunyun_liu@yahoo.com
GeneralRe: Selective tooltipssussAnonymous13 Aug '04 - 8:24 
Hey guys,
 
My name is Baron and i Just sent you an email about this bug. I hope you guys can help out.
 
Thanks in advance.
GeneralRe: Selective tooltipsmemberChunyun13 Aug '04 - 16:49 
Use Hans example. It will help you.
 
http://www.codeproject.com/combobox/XTipComboBox.asp?df=100&forumid=16044&select=897875&msg=897875
 
Good Luck!
Chunyun
 
Thanks!
General_mbstowcsz() on VS.NETmemberAmit Gefen22 Jan '04 - 11:24 
The function "_mbstowcsz()". Even it compiled on VS C++, it does not compiled on VS C++ .NET. Anybody know why? Also there is no sign for that function on MSDN (April 2003). Whats that function for? Is there any alternative? Confused | :confused:
 
Amit Gefen.
Project Lead - Software Engineer
Tel Aviv, Israel.

GeneralTooltip FlickeringmemberKaren03021 Jan '04 - 3:27 
I implemented my own version of tooltips for a Listbox before I saw your article but I am having a problem with the tooltip flickering in and out when it is displayed. I think that this is due to the system redrawing the listbox window all the time the tooltip is displayed.
 
My question is how did you get around this problem? I've looked zat your code and I can't find it. You help would be much appreciated.
 
Karen
GeneralEmpty list boxes go boom...sussDavant29 Apr '03 - 10:45 
I updated one of your methods to include a check for empty list boxes. In my case, I add list items after the fact. Here is the update.
 
BOOL CExListBox::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
if (GetCount() == 0)
return TRUE;
 

PS, thanks for writing the class. Sure did make my life easier.
 
~~~~~~~~
Now is not a good time to quit.
GeneralRe: Empty list boxes go boom...memberran wainstein3 May '03 - 23:17 
Thanks, man .
nice to hear some kind words , everybody elese here complaining Smile | :)
 
Ran. Smile | :) )
QuestionHow to Hook TTN_NEEDTEXT?suss4888558427 Apr '03 - 22:28 
I want to Hook TTN_NEEDTEXTA or TTN_NEEDTEXTW, and change the tooltip string dynamicly. I used SetWindowsHookEx( WH_CALLWNDPROC, Hook_ToolbarTip, NULL, GetCurrentThreadId()) to hook the WM_NOTIFY message, I get it, then from the code I found the TTN_NEEDTEXTW message, the code I wrote in the Hook_ToolbarTip was that:
LRESULT CALLBACK Hook_ToolbarTip(int code, WPARAM wParam, LPARAM lParam)
{
CWPSTRUCT* pTemp = (CWPSTRUCT*)lParam;
if(code == HC_ACTION )
{
HWND hWnd = pTemp->hwnd;
if( pTemp->message == WM_NOTIFY )
{
if( ((LPNMHDR)(pTemp->lParam))->code == TTN_NEEDTEXTA || ((LPNMHDR)(pTemp->lParam))->code == TTN_NEEDTEXTW )
{
NMHDR* pNMHDR = (NMHDR*)(pTemp->lParam);
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
CString strTipText = "hehe";
#ifndef _UNICODE
if (pNMHDR->code == TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText, strTipText, _countof(pTTTA->szText));
else
_mbstowcsz(pTTTW->szText, strTipText, _countof(pTTTW->szText));
#else
if (pNMHDR->code == TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText, strTipText, _countof(pTTTA->szText));
else
lstrcpyn(pTTTW->szText, strTipText, _countof(pTTTW->szText));
#endif
}
}
return CallNextHookEx( g_Hook, code, wParam, lParam);
}
But the tooltip text didn't change. What's the matter? How can I do, if I want to hook the massage, and change the text dynamicly? thank you.

QuestionWhy this project doesn't work on my machine? I cannot see the tooltip. Can you tell me ?sussAnonymous16 Oct '02 - 15:43 
Confused | :confused:

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 5 Jan 2002
Article Copyright 2002 by ran wainstein
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid