Tooltip for individual column header

CToolTipCtrl	m_tooltip;

Step 2: Create the tooltip object

Override PreSubclassWindow() in your CListCtrl derived class. After calling the base class PreSubclassWindow(), create the tooltip object. We override the PreSubclassWindow() instead of OnCreate() because the control is usually attached to the C++ object after it has already been created - usually from a dialog resource - and therefore OnCreate is never called for the object. It is important to note that the call to GetDlgItem(0) may fail if the control was created with a style other than LVS_REPORT.

If you are deriving from CListView then the code to create the tooltip and add a tool to it can be moved to OnCreate() or the OnInitialUpdate() function.

void CMyListCtrl::PreSubclassWindow() 
{
	CListCtrl::PreSubclassWindow();

	// Add initialization code
	m_tooltip.Create( this );
} 

Step 3: Call RelayEvents() function of the tooltip object

Override PreTranslateMessage() and call the RelayEvents() function of the CToolTipCtrl object. Calling RelayEvents() gives the tooltip an oppurtunity to determine whether the mouse entered any of the tool areas. Although we pass on every message that the list view control gets, the tooltip control processes only the WM_?BUTTONDOWN, WM_?BUTTONUP and the WM_MOUSEMOVE messages.
BOOL CMyListCtrl::PreTranslateMessage(MSG* pMsg) 
{
	m_tooltip.RelayEvent( pMsg );	
	return CListCtrl::PreTranslateMessage(pMsg);
}

Step 4: Add helper function to add tooltips

A single tooltip control can handle multiple tools or multiple rectangular areas for which a tooltip is needed. The helper function AddHeaderToolTip() simply adds a new tool to the tooltip control.
// AddHeaderToolTip	- Add a tooltip for the column header
//			  The control mode should be LVS_REPORT
// Returns		- TRUE on success
// nCol			- the column index
// sTip			- the tooltip text. A NULL will use the 
//			  column header text
BOOL CMyListCtrl::AddHeaderToolTip(int nCol, LPCTSTR sTip /*= NULL*)
{
	const int TOOLTIP_LENGTH = 80;
	char buf[TOOLTIP_LENGTH+1];

	CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
	int nColumnCount = pHeader->GetItemCount();
	if( nCol >= nColumnCount)
		return FALSE;

	if( (GetStyle() & LVS_TYPEMASK) != LVS_REPORT )
		return FALSE;

	// Get the header height
	RECT rect;
	pHeader->GetClientRect( &rect );
	int height = rect.bottom;

	RECT rctooltip;
	rctooltip.top = 0;
	rctooltip.bottom = rect.bottom;

	// Now get the left and right border of the column
	rctooltip.left = 0 - GetScrollPos( SB_HORZ );
	for( int i = 0; i < nCol; i++ )
		rctooltip.left += GetColumnWidth( i );
	rctooltip.right = rctooltip.left + GetColumnWidth( nCol );

	if( sTip == NULL )
	{
		// Get column heading
		LV_COLUMN lvcolumn;
		lvcolumn.mask = LVCF_TEXT;
		lvcolumn.pszText = buf;
		lvcolumn.cchTextMax = TOOLTIP_LENGTH;
		if( !GetColumn( nCol, &lvcolumn ) )
			return FALSE;
	}


	m_tooltip.AddTool( GetDlgItem(0), sTip ? sTip : buf, &rctooltip, nCol+1 );
	return TRUE;
}

Step 5: Update tooltip control whenever a column is resized

Override OnNotify() to track changes to the column widths. If we do not update the tooltip information after the user has finished risizing a column, the tooltip will no longer reflect the proper column.
BOOL CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
{
	HD_NOTIFY	*pHDN = (HD_NOTIFY*)lParam;

	if((pHDN->hdr.code == HDN_ENDTRACKA || pHDN->hdr.code == HDN_ENDTRACKW))
	{
		// Update the tooltip info
		CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
		int nColumnCount = pHeader->GetItemCount();

		CToolInfo toolinfo;
		toolinfo.cbSize = sizeof( toolinfo );

		// Cycle through the tooltipinfo for each effected column
		for( int i = pHDN->iItem; i <= nColumnCount; i++ )
		{
			m_tooltip.GetToolInfo( toolinfo, pHeader, i + 1 );

			int dx;				// store change in width
			if( i == pHDN->iItem )
				dx = pHDN->pitem->cxy - 
					(toolinfo.rect.right - toolinfo.rect.left);
			else 
				toolinfo.rect.left += dx;
			toolinfo.rect.right += dx;
			m_tooltip.SetToolInfo( &toolinfo );
		}
	}
	
	return CListCtrl::OnNotify(wParam, lParam, pResult);
}

Enhancement Step: Consolidate tooltip update code

This step was suggested by Roger Onslow and will help the code look more readable and less error prone. You will notice that the tooltip rectangle is calculated in two places - the AddHeaderToolTip() and OnNotify() functions.

Here's what Roger had to say.

The method you have on your site duplicates some code between the recalc routine and the creation routine.

I have actually split the common code into my RecalcHeaderTip method

I have a DefineColumn method which wraps up the column definition code. This method creates the tooltip for the column after defining it as follows...

     CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
     if (pHeader) {
          // Get the header height
          RECT rect; pHeader->GetClientRect(&rect);
          RECT rctooltip; rctooltip.top = 0; rctooltip.bottom = rect.bottom;
          rctooltip.left = 0 - GetScrollPos(SB_HORZ);
          for (int i = 0; i < col; i++ ) rctooltip.left += GetColumnWidth(i);
          rctooltip.right = rctooltip.left + GetColumnWidth(col);
          m_tooltip.AddTool(pHeader,tip?tip:text,&rctooltip,col+1);
     }

Then I call RecalcHeaderTip in my AutoSizeColumns and my OnNotify methods

Call this routine (below) to automatically size columns to fit the contents. (See also Autosize a column to fit its content) You should call this in you Endlabeledit handler or whenever the contents of the columns changes (eg after adding or deleting a row).

void CMyListCtrl::AutoSizeColumns() {
     // Call this after your list control is filled
     SetRedraw(false);   // turn off drawing while we update
     CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
     int numcol = pHeader->GetItemCount();
     for (int col = 0; col < numcol; col++) {
          SetColumnWidth(col,LVSCW_AUTOSIZE);
     }
     RecalcHeaderTips(); // update your header tips here if you have them
     SetRedraw(true);    // allow drawing
     Invalidate();       // and do the repaint
}

Note the call to RecalcHeaderTips. This updates header tool tips because the column positions have changed.

void CMyListCtrl::RecalcHeaderTips() {
     // Update the tooltip info
     CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
     RECT rect; pHeader->GetClientRect(&rect);
     RECT rctooltip; rctooltip.top = 0; rctooltip.bottom = rect.bottom;
     rctooltip.left = 0 - GetScrollPos(SB_HORZ);
     CToolInfo toolinfo; toolinfo.cbSize = sizeof(toolinfo);
     // Cycle through the tooltipinfo for each column
     int numcol = pHeader->GetItemCount();
     for (int col = 0; col <= numcol; col++ ) {
          m_tooltip.GetToolInfo(toolinfo,pHeader,col+1);
          rctooltip.right = rctooltip.left + GetColumnWidth(col);
          toolinfo.rect = rctooltip;
          m_tooltip.SetToolInfo (&toolinfo);
          rctooltip.left += GetColumnWidth(col);
     }
}

NOTE: you should also call this when you get a HDN_ENDTRACKA|W notify for manual column adjustment. For example

BOOL CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
     HD_NOTIFY *pHDN = reinterpret_cast(lParam);
     switch (pHDN->hdr.code) {
     // ...
     // other message handling here
     // ...
     case HDN_ENDTRACKA:
     case HDN_ENDTRACKW:
          {
               bool ok = CListCtrl::OnNotify(wParam, lParam, pResult);
               RecalcHeaderTips();
               return ok;
          }
          break;
     }
     return CListCtrl::OnNotify(wParam, lParam, pResult);
}



Comments

  • GHD cuidado, plancha ghd de la línea de productos para el cuidado del cabello son una gran opción

    Posted by bjisxa103 on 07/17/2013 06:41am

    GHD Pink Limited muestra siempre un aumento en gran tendencia esta temporada rosa es una tendencia esta temporada. Está equipado con características avanzadas tales como la mejora del control de temperatura para mantener el calor durante el peinado, un modo de espera y la tensión universal. Además, GHD es el estilo perfecto de la Nueva Hermosa rosa Styler Box Set es un secreto, porque todo está encerrado en una caja hermosa, por lo que este conjunto un regalo perfecto: Usted puede disfrutar de envío gratis a Dinamarca, no hay impuesto y guardar 50% off. Si usted tiene alguna pregunta, por favor póngase en contacto con nosotros. tenemos la capacidad de ofrecer el mejor servicio. No lo dude, aprovechar la oportunidad para comprar moda ghd planchas fladjern.ghd son el absoluto último en peluquería. Con planchas ghd, usted no está solo enderezar su cabello - que también puede utilizar su plancha para el cabello GHD para crear rizos grandes y voluminosos glamour, longitudes de movimiento, cuello de soldadura o un montón de volumen en la parte superior. [url=http://planchas-ghd.manifo.com/]planchas ghd baratas[/url] Aarhus que aman la cultura, los artículos de moda todo el tiempo, que están dispuestos a ser generosos temperament.They también tienen este problema, salir a nadar o esquiar etc proyecto de espectáculos inevitablemente perturbar el pelo original, lo que resulta en yourself.So muy embarazoso desea que un amigo, ya que pueden mantener los moment.Clothes perfectos y zapatos todo puede cambiar en cualquier pelo time.But? Aquí hemos introducido ghd glattejern.Resort son el must-have item.It no sólo es pequeña, de moda, conveniente para el voltaje de los países, el tiempo caliente es korte.som hasta diez minutos, que es el mismo que otro. [url=http://ghdplanchasde.blinkweb.com/]ghd baratas[/url] Incluso tengo un hecho BABYLISS yo hago una buena capa con una línea de rotación correspondiente al control de la temperatura y la deficiencia de la hormona del crecimiento performance, peeeero su tambor redondo es un dolor y el costo real, por lo que las olas o el pelo rizado ... La diferencia es comprobar en el último Hoy en día, fui a mi habitual estilista y le pregunté su cabello ondularan y eliminar un déficit de hormona de crecimiento (GOLD modelo Classic), fue una verdadera sorpresa al ver lo cómodo que parece utilizar la velocidad de la rapidez final, un resultado muy profesional .

    Reply
  • Los beneficios del uso de ghd, ghd comprar para crear la belleza del cabello

    Posted by hanmeihm on 05/30/2013 01:42pm

    [url=http://www.planchasghdbaratasonline.com/]ghd España[/url] usted puede hacer un ghd en Sephora sobre como $ 200but asegurarse de que son el estado de la t¦cnica. se alinean r¢pidamente, lo dejan suave y hacen-brillante. aydales increble, amo a todos. ] ghds son, evidentemente, alisadores de pelo maravillosas. su propio trabajo de enderezamiento es cool. tienen los ltimos modelos de y su pelo se mantenga recta durante m¢s tiempo. pero remember1. vas a enderezar su cabello todos los das 2. Tiene suficiente dinero para pagar un now3 ghd derecha. Qu¦ problema si un ser un poco menos caro, para casi el mismo resultsif usted contest s a dos de estas preguntas, te recomiendo un poco de negocios leadinger plancha de pelo. noches pelo satisfechos) [url=http://www.planchasespanaghdtop.net/ghdtienda/]GHD Plancha de pelo[/url] Junto con un acero Ghd, puede disear y labrar su propio pelo en casa sin tener que navegar por la tienda de la elegancia. Muchos de estos clubes nocturnos puede utilizarse en seco, suciedad, as como cerraduras abrazo estilos. Puede ser que sean muy fciles de encontrar, adems, casi todos los requisitos de peluquera. Usted puede elegir numerosos Ghd cerraduras clubes nocturnos. Usted est seguro de tener una plancha de golf que est completamente equipado para su especial de pelo y elegancia.Abra las reales GHD alisadores de pelo, que puedan tentadora. [url=http://www.planchasghdbaratasonline.com/blog/]Comprar por Ghd[/url] En el caso de que me estoy volviendo completamente brutalmente sincero, no podemos ver diferenciar junto con sus cerraduras en las planchas para el pelo finales la dama utilizado. Sin embargo todos modos, la seora me garantiza personalmente que son m¢s r¢pido para hacer uso de, mucho mejores platos de porcelana (evidentemente), mucho mejor el tratamiento y as sucesivamente. As que las grandes ocasiones. Si no est¢ seguro, haga clic en alza algunos GHDs as como podr¢ derrotar a la celebracin del cumpleaos de vacaciones al ao actual tensin.Preste especial atencin en el ltimo accesorio para los reales ghd sres queridos

    Reply
  • CToolTipCtrl::GetToolInfo() does not work under WinXP!

    Posted by Legacy on 01/04/2003 12:00am

    Originally posted by: Zhao Wei

    The function CToolTipCtrl::GetToolInfo() does not seem to work with Common Controls Version 6 under WinXP. 
    
    With Common Controls V5, the CToolInfo parameter in GetToolInfo() does contain information about
    the specified tool's text and rect, but with Common Controls V6, it doesn't.
    We can fix this bug by rewriting the function RecalcHeaderTips with a call to CToolTipCtrl::SetToolRect().

    void CHeaderToolTipListCtrl::RecalcHeaderTips()
    {
    CHeaderCtrl* pHeader = GetHeaderCtrl();
    RECT rctooltip;
    // Update all tools' rect
    int numcol = pHeader->GetItemCount();
    for (int col = 0; col < numcol; col++ ) {
    pHeader->GetItemRect(col, &rctooltip);
    m_tooltip.SetToolRect(pHeader, col+1, &rctooltip);
    }
    }

    If your CListCtrl supports LVS_EX_HEADERDRAGDROP style, we can use Ilya Kheifets' method:
    post a LVM_SETCOLUMNWIDTH message to the control so that the HDN_ITEMCHANGEDA/HDN_ITEMCHANGEDW notification
    will be received after the columns order has changed.

    BOOL CHeaderToolTipListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
    HD_NOTIFY *pHDN = (HD_NOTIFY*)lParam;

    switch(pHDN->hdr.code){
    case HDN_ITEMCHANGEDA:
    case HDN_ITEMCHANGEDW:
    RecalcHeaderTips();
    break;
    case HDN_ENDDRAG:
    {
    LPNMHEADER pNMHeader = (LPNMHEADER)pHDN;
    int nWidth = GetColumnWidth(pNMHeader->iItem);
    PostMessage(LVM_SETCOLUMNWIDTH, pNMHeader->iItem, nWidth);
    }
    break;
    }

    return CListCtrl::OnNotify(wParam, lParam, pResult);
    }


    Reply
  • www.tooltips.net

    Posted by Legacy on 12/19/2002 12:00am

    Originally posted by: Vitaly

    Just for further exploration of the subject, more tooltips can be found here: www.tooltips.net


    Reply
  • It doesn't work with tool tips longer than 80 characters

    Posted by Legacy on 06/24/2002 12:00am

    Originally posted by: Norbert Eggert

    Thanks for the source code, it was exactly what I need for my CListCtrl.
    
    I found in my Implementation a problem with tool text longer than 80 characters. It looks like a problem with compatibility with older versions of mfc. So I solve the problem with new phrasing of RecalcHeaderTips().


    void CToolTipListCtrl::RecalcHeaderTips()
    {
    // Update the tooltip info
    CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
    RECT rect; pHeader->GetClientRect(&rect);
    RECT rctooltip; rctooltip.top = 0; rctooltip.bottom = rect.bottom;
    rctooltip.left = 0 - GetScrollPos(SB_HORZ);
    int numcol = pHeader->GetItemCount();
    for (int col = 0; col <= numcol; col++)
    {
    rctooltip.right = rctooltip.left + GetColumnWidth(col);
    m_tooltip.SetToolRect(pHeader, col+1, &rctooltip);
    rctooltip.left += GetColumnWidth(col);
    }
    }

    Reply
  • Updated version: WTL, HEADERDRAGDROP

    Posted by Legacy on 05/18/2002 12:00am

    Originally posted by: Ilya Kheifets

    My version of ToolTipped header has following differences:
    1. It's based on WTL.
    2. It support LVS_EX_HEADERDRAGDROP style (drag-and-drop reordering of columns in a list-view control).
    3. It uses m_Header.GetItemRect (instead of GetScrollPos and GetColumnWidth).

    The only interesting moment is that we recieve HDN_ENDDRAG notification BEFORE postions of colomns have changed. So we post LVM_SETCOLUMNWIDTH in order to recieve HDN_ITEMCHANGED notification AFTER postions of colomns have changed.

    CToolTipCtrl m_HdrToolTip;
    CHeaderCtrl m_Header;

    BEGIN_MSG_MAP(CMyDlg)
    NOTIFY_CODE_HANDLER(HDN_ITEMCHANGED, OnHeaderChanged)
    NOTIFY_CODE_HANDLER(HDN_ENDDRAG, OnHeaderDraged)
    END_MSG_MAP()

    static LPCTSTR s_carszToolTips[];
    LPCTSTR* GetHdrToolTips()
    {
    return s_carszToolTips;
    }
    LPCTSTR s_carszToolTips[] = { "0", "1", "2" };

    void Init()
    {
    m_Header = GetList()->GetHeader();
    //add tooltip
    VERIFY( m_HdrToolTip.Create( m_Header, 0, 0, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, WS_EX_TOPMOST ) );

    for( int i=0; i<m_Header.GetItemCount(); ++i )
    {
    char szbuf[128]="";
    TOOLINFO ti={sizeof(ti)};
    IniToolInfo( i, ti, szbuf );
    VERIFY( m_HdrToolTip.AddTool( &ti ) );
    }
    }

    void IniToolInfo(int i, TOOLINFO& ti, LPTSTR pText)
    {
    VERIFY( m_Header.GetItemRect( i, &ti.rect ) );
    ti.uFlags = TTF_SUBCLASS;
    ti.hwnd = m_Header.m_hWnd;
    ti.uId = i;
    lstrcpy( pText, GetHdrToolTips()[i] );
    ti.lpszText = pText;
    }

    LRESULT OnHeaderChanged(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
    {
    if( !m_Header.IsWindow() || !m_HdrToolTip.IsWindow() )
    return 0;
    // change tool tip
    int iCnt = m_Header.GetItemCount();
    if(!iCnt)
    return 0;
    ASSERT( iCnt == m_HdrToolTip.GetToolCount() );
    for( int i=0; i<iCnt; i++ )
    {
    char szbuf[128]="";
    TOOLINFO ti={sizeof(ti)};
    IniToolInfo( i, ti, szbuf );
    m_HdrToolTip.SetToolInfo( &ti );
    }
    return 0;
    }

    LRESULT OnHeaderDraged(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
    {// force OnHeaderChanged to be called atfer new position of colomns are defined
    if( IsWindow( GetList()->m_hWnd ) )
    {
    int iw = GetList()->GetColumnWidth(0);
    GetList()->PostMessage(LVM_SETCOLUMNWIDTH, 0, MAKELPARAM(iw, 0));
    }
    return 0;
    }

    Reply
  • Bug in OnNotify + fix

    Posted by Legacy on 07/23/1999 12:00am

    Originally posted by: Sander Rorije

    Roger Onslow's last note is that column adjustments should be catched in order to call RecalcHeaderTips. This can be done by adding the cases HDN_ENDTRACKW and HDN_ENDTRACKA to the OnNotify method (and calling RecalcHeaderTips in those cases). I noticed that RecalcHeaderTips gets called indeed, but the tooltip "hotspot" rectangles remain unaffected. In other words, the call of RecalcHeaderTips has no effect.

    This is caused by the fact that during the call to RecalcHeaderTips (where GetColumnWidth is used to determine the new column widths), the columns widths aren't updated yet. To solve this, the HDN_ENDTRACKW and HDN_ENDTRACKA constants in OnNotify should be changed to HDN_ITEMCHANGEDW and HDN_ITEMCHANGEDA. Now RecalcHeaderTips gets called AFTER the column widths are updated, and everything works fine.

    Furthermore I would like to thank Codeguru, you've helped me a lot the last year. Keep up the good work!

    Reply
  • Placing m_tooltip.Create in PreSubclassWindow causes ASSERT failure

    Posted by Legacy on 11/07/1998 12:00am

    Originally posted by: Chip

    This article suggests putting the call to m_tooltip.Create() in PreSubclassWindow:
    
    

    void CMyListCtrl::PreSubclassWindow()
    {
    CListCtrl::PreSubclassWindow();

    // Add initialization code
    m_tooltip.Create( this );
    }

    However, when I attempted to implement this, an assertion in AfxWindowProc() failed on the call to Create(). I moved the call to CMyListCtrl::OnCreate() and that cleared up the problem.

    Reply
  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds