ListBox With ToolTip Support






4.78/5 (17 votes)
Jan 5, 2002
2 min read

219971

4189
A CListBox derived class providing Item tooltips
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:
- Add ExListBox.cpp and ExListBox.h to your project.
- Add include "ExListBox.h" to the header file of your dialog class.
- Add a list box control to your dialog.
- Use the ClassWizard to add a member variable of type CListBox for the list box you just added.
- 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; }