CTreeCtrl With ToolTip Based On the Item Data

Environment: VC6 SP4, NT4 SP3,Win 95/98/ME

Introduction

Let's say you want a tree ctrl with tool tip support. You can find some good articles explaining you exactly what to do ( look in the treeview section in code guru, Zafir Anjum's article will give you all the information you need. What if you want to display a custom tool tip for each item? One which will display any kind of data and not only the tree item text?

I searched for a class that would give me the freedom to display any tool tip I want and couldn't find any. You can use this CTreeCtrl derived class if you have the same problam.

This article will not focus on the technical sides of adding tool tip support to your tree ctrl. For that, use the articles I've listed above.

Before starting, I would like to thanks Yaniv Ben Ari for his great idea.

What is this Class?

CTreeCtrlCh is a CTreeCtrl derived class, which display tool tip according to the item data. You can attach any string you like to the any tree item using SetItemData() function. This text will be displayed as a tool tip.

How this class works?

This class uses an abstract class as an ItemData templete. The user must derived it's own ItemData class from the abstract class, and must implement a virtual function called : GetToolTipString(). This function returns a CString object that will be displayed as a tool tip. The user should construct this returned CString object according to his Own ItemData class. Sound a little complicated? It's not, as the next section will show you.

How To Use This Class?

  • Add the files you've downloaded to your project directoy and add them to your project.
  • Add a TreeCtrl to your dialog and assign it a member using the control wizard
  • Add the following statement to your dialog header file:
    #include "TreeCtrlCh.h"
  • Change the tree control variable type to CTreeCtrlch
  • In the dialog header file construct a new class derived from ItemDataABSTRACT as public. This class will be use as your tree item data. Add any data member you want. Most important is to add the GetToolTipString(), (pure virtual, must be implemented)
  • In your dialog implementation file, you need to implement the GetToolTipString() function. This function MUST return a CString object, this object will be displayed as the item tool tip. In this function you can manipulate the item data members and construct any string you want such as additional information on the item, dates, size, and etc.
  • When you to add item data, declare a pointer to the class you've derived from ItemDataABSTRACT. Assign data to it's memberand use SetItemData to add the ItemData pointer.
  • There is no need to delete the memory allocated, the CTreeCtrlch will free all allocated memory used for item data.

 

#include "stdafx.h"
#include "TreeCtrlCh.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////
// CTreeCtrlCh
CTreeCtrlCh::CTreeCtrlCh()
{
}
CTreeCtrlCh::~CTreeCtrlCh()
{
}
BEGIN_MESSAGE_MAP(CTreeCtrlCh, CTreeCtrl)
   //{{AFX_MSG_MAP(CTreeCtrlCh)
   ON_WM_LBUTTONDOWN()
   ON_WM_DESTROY()
   //}}AFX_MSG_MAP
   ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 
                      0, 
                      0xFFFF, 
                      OnToolTipText)
   ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 
                      0, 
                      0xFFFF, 
                      OnToolTipText)
END_MESSAGE_MAP()
///////////////////////////////////////
// CTreeCtrlCh message handlers
void CTreeCtrlCh::PreSubclassWindow() 
{
   // TODO: Add your specialized code here 
   // and/or call the base class
   CTreeCtrl::PreSubclassWindow();
   EnableToolTips(TRUE);
}
int CTreeCtrlCh::OnToolHitTest(CPoint point, 
                               TOOLINFO * pTI) const
{
   RECT rect;
   UINT nFlags;
   HTREEITEM hitem = HitTest( point, &nFlags );
   if( nFlags & TVHT_ONITEMLABEL  )
   {
      GetItemRect( hitem, &rect, TRUE );
      pTI->hwnd = m_hWnd;
      pTI->uId = (UINT)hitem;
      pTI->lpszText = LPSTR_TEXTCALLBACK;
      pTI->rect = rect;
      return pTI->uId;
   }
      return -1;
}
//here we supply the text for the item 
BOOL CTreeCtrlCh::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;
   // Do not process the msg from built in tooltip 
   if( nID == (UINT)m_hWnd &&
      (( pNMHDR->code == TTN_NEEDTEXTA && 
         pTTTA->uFlags & TTF_IDISHWND ) ||
      ( pNMHDR->code == TTN_NEEDTEXTW && 
        pTTTW->uFlags & TTF_IDISHWND ) ) )
      return FALSE;
   // Get the mouse position
   const MSG* pMessage;
   CPoint pt;
   pMessage = GetCurrentMessage(); // get mouse pos 
   ASSERT ( pMessage );
   pt = pMessage->pt;
   ScreenToClient( &pt );
   UINT nFlags;
   HTREEITEM hitem = 
       HitTest( pt, &nFlags ); //Get item pointed by mouse
   strTipText.Format( "%s", 
          GetItemText( (HTREEITEM ) nID)); //get item text
//// here we take the item data , cast it to 
//// ItemDataABSTRACT and call the virtual function
   DWORD dw = 
      GetItemData((HTREEITEM ) nID); //get item data
   ItemDataABSTRACT* ItemData = 
            (ItemDataABSTRACT*)dw; //CAST item data
   if (ItemData!=NULL)
   {
      CString s = 
         ItemData->GetToolTipString(); //pure virtual function
      strTipText=" " + s; //add node text to node data text
   }
#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;    // message was handled
}

//delete all allocated mamory for item data 
void CTreeCtrlCh::CleanItemData()
{
   HTREEITEM root = GetFirstVisibleItem();
   if (root==NULL)
      return ;
   do 
   {
      
      DWORD data = GetItemData(root);
      if (data!=NULL)
      {
         ItemDataABSTRACT* ItemData = 
                          (ItemDataABSTRACT*)data;

         delete ItemData ;
      }
      DeleteBranchData(root);
      
   }while ((root = GetNextSiblingItem(root))!=NULL);

}
void CTreeCtrlCh::OnDestroy() 
{
   CleanItemData();
   CTreeCtrl::OnDestroy();
   
   // TODO: Add your message handler code here
   
}
void CTreeCtrlCh::DeleteBranchData( HTREEITEM hti)
{
   if( ItemHasChildren( hti ) )
   {
         
      hti = GetChildItem( hti );
      do
      {

         DWORD data = GetItemData(hti);
         if (data!=NULL)
         {
            ItemDataABSTRACT* ItemData = 
                        (ItemDataABSTRACT*)data;

            delete ItemData ;
         }

      
         DeleteBranchData( hti);
      }while( (hti = GetNextSiblingItem( hti )) != NULL );
   }
        
}
/////////////////HEADER FILE///////////////////
class ItemDataABSTRACT
{

   public:
   virtual CString GetToolTipString() = 0;
      
   virtual ~ItemDataABSTRACT(){};

   protected:
      ItemDataABSTRACT(){}; 
   
};

class CTreeCtrlCh : public CTreeCtrl
{
// Construction
public:
   CTreeCtrlCh();
   int OnToolHitTest(CPoint point, TOOLINFO * pTI) const;
   void CleanItemData();
   BOOL OnToolTipText( UINT id, 
                       NMHDR * pNMHDR, 
                  LRESULT * pResult );
   void DeleteBranchData( HTREEITEM hti);

// Attributes
public:

// Operations
public:

// Overrides
   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CTreeCtrlCh)
   protected:
   virtual void PreSubclassWindow();
   //}}AFX_VIRTUAL
// Implementation
public:
   virtual ~CTreeCtrlCh();
   // Generated message map functions
protected:
   //{{AFX_MSG(CTreeCtrlCh)
   afx_msg void OnDestroy();
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

Downloads

Download demo project - 30.1 Kb
Download source - 2.43 Kb


Comments

  • Still get memory leaks

    Posted by Legacy on 07/10/2003 12:00am

    Originally posted by: Dietmar

    I still get memory leaks.
    These hints did not help:

    http://codeguru.earthweb.com/mfc/comments/37203.shtml

    http://www.codeproject.com/treectrl/treetooltip.asp?df=100&forumid=3180&select=535235#xx535235xx

    Any suggestions?

    Reply
  • memory leak bug fixed

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

    Originally posted by: Wei Junping

    void CTreeCtrlCh::CleanItemData()
    
    {

    HTREEITEM root = GetRootItem();
    }

    Reply
  • CtreeCtrl and PropertyPage

    Posted by Legacy on 02/28/2002 12:00am

    Originally posted by: matteo

    I have a class in a PropertyPage that extend a CtreeCtrl ,
    and I have problem with tooltip example, in particular :

    - OnToolHitTest function is never call

    - in OnToolTipText function nID = pNMHDR->idFrom is everytime
    like m_hWnd. If I delete the code

    if( nID == (UINT)m_hWnd &&
    (( pNMHDR->code == TTN_NEEDTEXTA && pTTTA->uFlags & TTF_IDISHWND ) ||
    ( pNMHDR->code == TTN_NEEDTEXTW && pTTTW->uFlags & TTF_IDISHWND ) ) )
    return FALSE;

    I can retrive hitem from HitTest but
    GetItemData((HTREEITEM ) nID) retrive alwais NULL.

    In any case if I set strTipText manually I see the tooltip, when I move
    mouse the OnToolTipText function is called but the new tooltip appear only
    if before, I exit from CtreeCtrl area

    What can I do ??

    Thanks in advance

    P.S: is my first OCX ;-)

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

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • A modern mobile IT strategy is no longer an option, it is an absolute business necessity. Today's most productive employees are not tied to a desk, an office, or a location. They are mobile. And your company's IT strategy has to be ready to support them with easy, reliable, 24/7 access to the business information they need, from anywhere in the world, across a broad range of communication devices. Here's how some of the nation's most progressive corporations are meeting the many needs of their mobile workers …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds