怎样给VC中的List控件添加ToolTip

  VC6的List控件默认是不能为subitem提供tooltip的,只有通过重写CListCtrl类来实现。在网上找了一个写好的CToolTipListCtrl类可以显示该功能,只需调用即可。具体步骤如下:

  1.将ToolTipListCtrl.h和ToolTipListCtrl.cpp加入工程。

  2.为List控件添加相应的变量CListCtrl m_lstObject。

  3.用CToolTipListCtrl替换上面的CListCtrl,当然还要加入相应的头文件“#include "ToolTipListCtrl.h"”。

  4.设置列表的扩展样式,使之包含LVS_EX_INFOTIP样式。

    m_lstObject.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_INFOTIP);

  5.步骤4也可以改为

    m_lstObject.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

    m_lstObject.EnableToolTips(TRUE);

  6.编译运行程序。效果见下图:

  是不是很简单呢,你也赶快试试吧。

  下面是ToolTipListCtrl.h和ToolTipListCtrl.cpp源码:

#if !defined(AFX_TOOLTIPLISTCTRL_H__EA17BA6D_ADD2_49E3_AB67_45B65316D19F__INCLUDED_)
#define AFX_TOOLTIPLISTCTRL_H__EA17BA6D_ADD2_49E3_AB67_45B65316D19F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// ToolTipListCtrl.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CToolTipListCtrl window

#include <afxtempl.h>

/////////////////////////////////////////////////////////////////////////////
// CToolTipListCtrl, v.1.0
// 
// A CListCtrl derived class that
// can display per SubItem tooltips by itself
//
// Author: Jo鉶 Filipe de Castro Ferreira (jfilipe@isr.uc.pt)
// Based on Nate Maynard's (nate.maynard@neomation.com) CToolTipTreeCtrl
//
// Last Modified: 7/11/2001
//
// License: Quoting Nate Maynard,
//  "use it however you want. If it helps you out, drop me a line and let me know. :-)"
//
// Disclaimer: This code comes with no warranty of any kind whatsoever. Use at your own risk.
//
/////////////////////////////////////////////////////////////////////////////

//The initial state of m_wHitMask
#define INITIAL_HITMASK LVHT_ONITEMLABEL

class CToolTipListCtrl : public CListCtrl
{
// Construction
public:
    CToolTipListCtrl();

// Attributes
public:

protected:
    // Map's SubItems to related tooltip text
    CMapStringToString m_ToolTipMap; 
    // A bit mask of LVHT_* flags the control will show tooltips for
    WORD m_wHitMask; 

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CToolTipListCtrl)
    //}}AFX_VIRTUAL

// Implementation
public:


    //** CWnd Overrides **
    //Provide our own logic for HitTests, specifically, make ToolHitTests respond per SubItem
    virtual int OnToolHitTest(CPoint point, TOOLINFO * pTI) const;

    
    //** CTreeCtrl Overrides **
    //Overriding the Delete functions makes sure m_ToolTipMap doesn't have excess mappings
    virtual BOOL DeleteAllItems( );
    virtual BOOL DeleteItem( int nItem );
    virtual BOOL SetItemText( int nItem, int nSubItem, LPTSTR lpszText );


    //** Additional Functions **

    //Set the TVHT_* flags that will trigger the display of a tooltip
    WORD SetToolTipHitMask(WORD wHitMask);
    //Clear all tooltips
    virtual void DeleteAllToolTips();
    //Set the tooltip text for a specific SubItem
    virtual BOOL SetItemToolTipText( int nItem, int nSubItem, LPCTSTR lpszToolTipText ); 
    //Retrieves the tooltip text for a specific SubItem 
    virtual CString GetItemToolTipText( int nItem, int nSubItem ); 

    
    virtual ~CToolTipListCtrl();

    // Generated message map functions
protected:
    //{{AFX_MSG(CToolTipListCtrl)
        // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG

    //Respondes to the TTN_NEEDTEXT* messages, provides the text of a tooltip
    virtual afx_msg BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult ); 

    DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TOOLTIPLISTCTRL_H__EA17BA6D_ADD2_49E3_AB67_45B65316D19F__INCLUDED_)
// ToolTipListCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "ToolTipListCtrl.h"

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

/////////////////////////////////////////////////////////////////////////////
// CToolTipListCtrl

CToolTipListCtrl::CToolTipListCtrl()
{
    m_wHitMask = INITIAL_HITMASK;
}

CToolTipListCtrl::~CToolTipListCtrl()
{
    // Cleanup when destroyed

    DeleteAllToolTips();
}

BEGIN_MESSAGE_MAP(CToolTipListCtrl, CListCtrl)
    //{{AFX_MSG_MAP(CToolTipListCtrl)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    //}}AFX_MSG_MAP

    //Trap all TTN_NEEDTEXT* Messages
    //TTN_NEEDTEXT* messages are sent when a ToolTipCtrl wants a control
    //to provide it with text to display as the tooltip.
    //Specifically, when the TOOLINFO structure passed back to the ToolTipCtrl
    //after ::OnToolHitTest has it's lpszText memeber set to LPSTR_TEXTCALLBACK.

    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
    ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CToolTipListCtrl message handlers

int CToolTipListCtrl::OnToolHitTest(CPoint point, TOOLINFO * pTI) const 
{
    // See if the point falls onto a list item
    //UINT nFlags = 0;
    
    LVHITTESTINFO lvhitTestInfo;
    
    lvhitTestInfo.pt = point;
    
    int nItem = ListView_SubItemHitTest(
        this->m_hWnd,
        &lvhitTestInfo);
    int nSubItem = lvhitTestInfo.iSubItem;
    
    UINT nFlags = lvhitTestInfo.flags;
    
    // nFlags is 0 if the SubItemHitTest fails
    // Therefore, 0 & <anything> will equal false
    if (nFlags & m_wHitMask)
    {
        // If it did fall on a list item,
        // and it was also hit one of the
        // item specific sub-areas we wish to show tool tips for
        
        // Get the client area occupied by this control
        RECT rcClient;
        GetClientRect( &rcClient );
        
        // Fill in the TOOLINFO structure
        pTI->hwnd = m_hWnd;
        pTI->uId = (UINT) (nItem * 100 + nSubItem);
        pTI->lpszText = LPSTR_TEXTCALLBACK;
        pTI->rect = rcClient;
        
        return pTI->uId; // By returning a unique value per listItem,
        // we ensure that when the mouse moves over another list item,
        // the tooltip will change
    }
    else
    {
        // Otherwise, we aren't interested, so let the message propagate
        return -1;
    }
}

BOOL CToolTipListCtrl::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
    // Handle both ANSI and UNICODE versions of the message
    TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
    TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
    
    // Ignore messages from the built in tooltip, we are processing them internally
    if( (pNMHDR->idFrom == (UINT)m_hWnd) &&
        ( ((pNMHDR->code == TTN_NEEDTEXTA) && (pTTTA->uFlags & TTF_IDISHWND)) ||
        ((pNMHDR->code == TTN_NEEDTEXTW) && (pTTTW->uFlags & TTF_IDISHWND)) ) )
    {
        return FALSE;
    }
    
    *pResult = 0;
    
    CString strTipText;
    
    // Get the mouse position
    const MSG* pMessage;
    pMessage = GetCurrentMessage();
    ASSERT ( pMessage );
    CPoint pt;
    pt = pMessage->pt; // Get the point from the message
    ScreenToClient( &pt ); // Convert the point's coords to be relative to this control
    
    // See if the point falls onto a list item
    
    LVHITTESTINFO lvhitTestInfo;
    
    lvhitTestInfo.pt = pt;
    
    int nItem = SubItemHitTest(&lvhitTestInfo);
    int nSubItem = lvhitTestInfo.iSubItem;
    
    UINT nFlags = lvhitTestInfo.flags;
    
    // nFlags is 0 if the SubItemHitTest fails
    // Therefore, 0 & <anything> will equal false
    if( nFlags & m_wHitMask )
    {
        // If it did fall on a list item,
        // and it was also hit one of the
        // item specific sub-areas we wish to show tool tips for
        
        // Lookup the list item's text in the ToolTip Map
        
        CString strKey;
        
        strKey.Format(_T("%d"),  nItem * 100 + nSubItem);
        
        if( m_ToolTipMap.Lookup(strKey, strTipText ) )
        {
            // If there was a CString associated with the list item,
            // copy it's text (up to 80 characters worth, limitation of the TOOLTIPTEXT structure)
            // into the TOOLTIPTEXT structure's szText member
            
            // Deal with UNICODE
#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
            return FALSE;    // We found a tool tip,
            // tell the framework this message has been handled
            
            ////////////////////////////////////////////////////////////////////////////////
            // ****** Special note *****
            //
            // Still don't understand why the function must return FALSE for CListCtrl
            // so as not to cause flickering, as opposed to Nate Maynard's derivation
            // from CTreeCtrl.
            // I have experimented with disabling Tooltips for the control
            // and found out that a "ghost" tooltip appears for a fraction of a second...
            //
            // I am completely at a loss...
            // Seems to work, though...
            //
            ////////////////////////////////////////////////////////////////////////////////
            
        }
    }
    
    return FALSE; // We didn't handle the message,
    // let the framework continue propagating the message
}

// Sets the tooltip text for a specific item
BOOL CToolTipListCtrl::SetItemToolTipText( int nItem, int nSubItem, LPCTSTR lpszToolTipText )
{
    CString strKey;
    
    strKey.Format(_T("%d"),  nItem * 100 + nSubItem);
    
    m_ToolTipMap.SetAt( strKey, lpszToolTipText );
    
    return TRUE;
}

// Retrieve the tooltip text for a specific list item
CString CToolTipListCtrl::GetItemToolTipText( int nItem, int nSubItem )
{
    CString itemToolTipText;
    
    CString strKey;
    
    strKey.Format(_T("%d"),  nItem * 100 + nSubItem);
    
    
    if( !m_ToolTipMap.Lookup( strKey, itemToolTipText ) )
    {
        itemToolTipText = "";
    }
    
    return itemToolTipText;
}

WORD CToolTipListCtrl::SetToolTipHitMask( WORD wHitMask )
{
    WORD oldHitMask = m_wHitMask;
    
    m_wHitMask = wHitMask;
    
    return oldHitMask;
}

void CToolTipListCtrl::DeleteAllToolTips()
{
    m_ToolTipMap.RemoveAll();
}

BOOL CToolTipListCtrl::DeleteAllItems( )
{
    // Call the base class method
    BOOL retVal = CListCtrl::DeleteAllItems();
    
    if( retVal )
    {
        // If it succeeded, remove all tooltips
        DeleteAllToolTips();
    }
    
    return retVal;
}

BOOL CToolTipListCtrl::DeleteItem( int nItem )
{
    // Call the base class method
    BOOL retVal = CListCtrl::DeleteItem( nItem );
    
    if( retVal )
    {
        // If it succeeded, remove it's tooltip from the map
        LVCOLUMN *lvColumn = (LVCOLUMN*) malloc (sizeof(LVCOLUMN));
        
        for (int i = 0; GetColumn(i, lvColumn) != 0; i++);
        {
            CString strKey;
            
            strKey.Format(_T("%d"),  nItem * 100 + i);
            
            m_ToolTipMap.RemoveKey( strKey );
        }
        // 李鸿喜2012年11月30日添加
        free( lvColumn );
    }
    
    return retVal;
}

/*
 */
BOOL CToolTipListCtrl::SetItemText( int nItem, int nSubItem, LPTSTR lpszText )
{
    // Call the base class method
    BOOL retVal = CListCtrl::SetItemText( nItem, nSubItem, lpszText );
    
    if( retVal )
    {
        SetItemToolTipText( nItem, nSubItem, lpszText );
    }
    
    return retVal;
}

  本程序在WinXP SP3+VC6下测试通过。

posted on 2012-11-29 20:31  onedime  阅读(2303)  评论(0编辑  收藏  举报