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()
{
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;
BOOL tmp = FALSE;
row = ItemFromPoint(point,tmp);
if ( row == -1 )
return -1;
GetItemRect(row,&cellrect);
pTI->rect = cellrect;
pTI->hwnd = m_hWnd;
pTI->uId = (UINT)((row));
pTI->lpszText = LPSTR_TEXTCALLBACK;
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)
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 )
{
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;
}
The source code
#include "ExListBox.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CExListBox::CExListBox()
{
}
CExListBox::~CExListBox()
{
}
BEGIN_MESSAGE_MAP(CExListBox, CListBox)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()
void CExListBox::PreSubclassWindow()
{
CListBox::PreSubclassWindow();
EnableToolTips(TRUE);
}
int CExListBox::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
{
int row;
RECT cellrect;
BOOL tmp = FALSE;
row = ItemFromPoint(point,tmp);
if ( row == -1 )
return -1;
GetItemRect(row,&cellrect);
pTI->rect = cellrect;
pTI->hwnd = m_hWnd;
pTI->uId = (UINT)((row));
pTI->lpszText = LPSTR_TEXTCALLBACK;
return pTI->uId;
}
BOOL CExListBox::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
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
{
int 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;
}