一、CListCtrl添加多行提示类
//ToolTipListCtrl.h
#pragma once
// CToolTipListCtrl
class CToolTipListCtrl : public CListCtrl
{
DECLARE_DYNAMIC(CToolTipListCtrl)
public:
CToolTipListCtrl();
virtual ~CToolTipListCtrl();
virtual int OnToolHitTest(CPoint point, TOOLINFO * pTI) const;
protected:
virtual afx_msg BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult );
virtual void PreSubclassWindow();
DECLARE_MESSAGE_MAP()
private:
CString m_strToolTipText;//Item提示信息
};
//ToolTipListCtrl.cpp
#include "stdafx.h"
#include "ToolTipListCtrl.h"
#include <atlconv.h>
// CToolTipListCtrl
IMPLEMENT_DYNAMIC(CToolTipListCtrl, CListCtrl)
CToolTipListCtrl::CToolTipListCtrl()
{
}
CToolTipListCtrl::~CToolTipListCtrl()
{
}
BEGIN_MESSAGE_MAP(CToolTipListCtrl, CListCtrl)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()
// CToolTipListCtrl 消息处理程序
void CToolTipListCtrl::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
// Disable the CToolTipCtrl of CListCtrl so it won't disturb our own tooltip-ctrl
GetToolTips()->Activate(FALSE);
}
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 & LVHT_ONITEMLABEL){
//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 ){
//VC6.0则用下面这句
//_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
//VC2003则用这句
AFX_MODULE_THREAD_STATE* pThreadState = AfxGetModuleThreadState();
CToolTipCtrl *pToolTip = pThreadState->m_pToolTip;
pToolTip->SetMaxTipWidth(500);
//Handle both ANSI and UNICODE versions of the message
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
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;
//Get the mouse position
const MSG* pMessage;
pMessage = GetCurrentMessage();
ASSERT ( pMessage );
CPoint pt;
pt = pMessage->pt; //Get the point from the message
ScreenToClient( &pt );
LVHITTESTINFO lvhitTestInfo;
lvhitTestInfo.pt = pt;
int nItem = SubItemHitTest(&lvhitTestInfo);
int nSubItem = lvhitTestInfo.iSubItem;
UINT nFlags = lvhitTestInfo.flags;
if( nFlags & LVHT_ONITEMLABEL ){
//设置提示信息显示的内容
m_strToolTipText = GetItemText(nItem,nSubItem);
//pTTTA->lpszText = (LPSTR)(LPCSTR)m_strToolTipText; //releas 会出现乱码,用下面方式替换
char szMsg[512] = {0};
strcpy(szMsg,m_strToolTipText.GetBuffer());
pTTTA->lpszText = szMsg;
m_strToolTipText.ReleaseBuffer();
USES_CONVERSION;
pTTTW->lpszText = A2W(m_strToolTipText);
return FALSE;
}
return FALSE;
}
二、CListCtrl内嵌ToolTipCtrl自定义提示类
//ListCtrl_OwnToolTipCtrl.h
//ListCtrl_OwnToolTipCtrl.h
#pragma once
#include "ListCtrl_ToolTip.h"
class CListCtrl_OwnToolTipCtrl : public CListCtrl_ToolTip
{
int m_LastToolTipCol;
int m_LastToolTipRow;
CToolTipCtrl m_OwnToolTipCtrl;
virtual void PreSubclassWindow();
virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg BOOL OnToolNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult);
DECLARE_MESSAGE_MAP();
public:
CListCtrl_OwnToolTipCtrl()
:m_LastToolTipCol(-1)
,m_LastToolTipRow(-1)
{}
};
//ListCtrl_OwnToolTipCtrl.cpp
#include "stdafx.h"
#include "ListCtrl_OwnToolTipCtrl.h"
// Maintain our own CToolTipCtrl
BEGIN_MESSAGE_MAP(CListCtrl_OwnToolTipCtrl, CListCtrl_OwnToolTipCtrl)
ON_WM_MOUSEMOVE()
ON_NOTIFY_EX(TTN_NEEDTEXTA, 0, OnToolNeedText)
ON_NOTIFY_EX(TTN_NEEDTEXTW, 0, OnToolNeedText)
END_MESSAGE_MAP()
void CListCtrl_OwnToolTipCtrl::PreSubclassWindow()
{
CListCtrl_ToolTip::PreSubclassWindow();
// Disable the CToolTipCtrl of CListCtrl so it won't disturb our own tooltip-ctrl
GetToolTips()->Activate(FALSE);
// Enable our own tooltip-ctrl and make it show tooltip even if not having focus
VERIFY( m_OwnToolTipCtrl.Create(this, TTS_ALWAYSTIP) );
m_OwnToolTipCtrl.Activate(TRUE);
}
BOOL CListCtrl_OwnToolTipCtrl::PreTranslateMessage(MSG* pMsg)
{
if (m_OwnToolTipCtrl.m_hWnd)
m_OwnToolTipCtrl.RelayEvent(pMsg);
return CListCtrl::PreTranslateMessage(pMsg);
}
void CListCtrl_OwnToolTipCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
CPoint pt(GetMessagePos());
ScreenToClient(&pt);
// Find the subitem
LVHITTESTINFO hitinfo = {0};
hitinfo.flags = nFlags;
hitinfo.pt = pt;
SubItemHitTest(&hitinfo);
if (m_LastToolTipCol!=hitinfo.iSubItem || m_LastToolTipRow!=hitinfo.iItem)
{
// Mouse moved over a new cell
m_LastToolTipCol = hitinfo.iSubItem;
m_LastToolTipRow = hitinfo.iItem;
// Remove the old tooltip (if available)
if (m_OwnToolTipCtrl.GetToolCount()>0)
{
m_OwnToolTipCtrl.DelTool(this);
m_OwnToolTipCtrl.Activate(FALSE);
}
// Add the new tooltip (if available)
if (m_LastToolTipRow!=-1 && m_LastToolTipRow!=-1)
{
// Not using CToolTipCtrl::AddTool() because it redirects the messages to CListCtrl parent
TOOLINFO ti = {0};
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND; // Indicate that uId is handle to a control
ti.uId = (UINT_PTR)m_hWnd; // Handle to the control
ti.hwnd = m_hWnd; // Handle to window to receive the tooltip-messages
ti.hinst = AfxGetInstanceHandle();
ti.lpszText = LPSTR_TEXTCALLBACK;
m_OwnToolTipCtrl.SendMessage(TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
m_OwnToolTipCtrl.Activate(TRUE);
}
}
CListCtrl::OnMouseMove(nFlags, point);
}
BOOL CListCtrl_OwnToolTipCtrl::OnToolNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
return CListCtrl_ToolTip::OnToolNeedText(id, pNMHDR, pResult);
}
//ListCtrl_ToolTip.h
#pragma once
class CListCtrl_ToolTip : public CListCtrl
{
public:
afx_msg BOOL OnToolNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult);
virtual void PreSubclassWindow();
void CellHitTest(const CPoint& pt, int& nRow, int& nCol) const;
bool ShowToolTip(const CPoint& pt) const;
CString GetToolTipText(int nRow, int nCol);
};
//ListCtrl_ToolTip.cpp
#include "stdafx.h"
#include "CListCtrl_ToolTip.h"
void CListCtrl_ToolTip::CellHitTest(const CPoint& pt, int& nRow, int& nCol) const
{
nRow = -1;
nCol = -1;
LVHITTESTINFO lvhti = {0};
lvhti.pt = pt;
nRow = ListView_SubItemHitTest(m_hWnd, &lvhti); // SubItemHitTest is non-const
nCol = lvhti.iSubItem;
if (!(lvhti.flags & LVHT_ONITEMLABEL))
nRow = -1;
}
BOOL CListCtrl_ToolTip::OnToolNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
CPoint pt(GetMessagePos());
ScreenToClient(&pt);
int nRow, nCol;
CellHitTest(pt, nRow, nCol);
CString tooltip = GetToolTipText(nRow, nCol);
if (tooltip.IsEmpty())
return FALSE;
// Non-unicode applications can receive requests for tooltip-text in unicode
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
#ifndef _UNICODE
if (pNMHDR->code == TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText, static_cast<LPCTSTR>(tooltip), sizeof(pTTTA->szText));
else
_mbstowcs(pTTTW->szText, static_cast<LPCTSTR>(tooltip), sizeof(pTTTW->szText)/sizeof(WCHAR));
#else
if (pNMHDR->code == TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText, static_cast<LPCTSTR>(tooltip), sizeof(pTTTA->szText));
else
lstrcpyn(pTTTW->szText, static_cast<LPCTSTR>(tooltip), sizeof(pTTTW->szText)/sizeof(WCHAR));
#endif
// If wanting to display a tooltip which is longer than 80 characters,
// then one must allocate the needed text-buffer instead of using szText,
// and point the TOOLTIPTEXT::lpszText to this text-buffer.
// When doing this, then one is required to release this text-buffer again
return TRUE;
}
bool CListCtrl_ToolTip::ShowToolTip(const CPoint& pt) const
{
// Lookup up the cell
int nRow, nCol;
CellHitTest(pt, nRow, nCol);
if (nRow!=-1 && nCol!=-1)
return true;
else
return false;
}
CString CListCtrl_ToolTip::GetToolTipText(int nRow, int nCol)
{
if (nRow!=-1 && nCol!=-1)
return GetItemText(nRow, nCol); // Cell-ToolTip
else
return CString("");
}
namespace {
LRESULT EnableWindowTheme(HWND hwnd, LPCWSTR classList, LPCWSTR subApp, LPCWSTR idlist)
{
LRESULT lResult = S_FALSE;
HMODULE hinstDll;
BOOL (WINAPI *pIsThemeActive)();
HRESULT (WINAPI *pSetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
HANDLE (WINAPI *pOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
HRESULT (WINAPI *pCloseThemeData)(HANDLE hTheme);
// Check if running on Windows XP or newer
hinstDll = ::LoadLibrary(_T("UxTheme.dll"));
if (hinstDll)
{
// Check if theme service is running
(FARPROC&)pIsThemeActive = ::GetProcAddress( hinstDll, "IsThemeActive" );
if( pIsThemeActive && pIsThemeActive() )
{
(FARPROC&)pOpenThemeData = ::GetProcAddress(hinstDll, "OpenThemeData");
(FARPROC&)pCloseThemeData = ::GetProcAddress(hinstDll, "CloseThemeData");
(FARPROC&)pSetWindowTheme = ::GetProcAddress(hinstDll, "SetWindowTheme");
if (pSetWindowTheme && pOpenThemeData && pCloseThemeData)
{
// Check is themes is available for the application
HANDLE hTheme = pOpenThemeData(hwnd,classList);
if (hTheme!=NULL)
{
VERIFY(pCloseThemeData(hTheme)==S_OK);
// Enable Windows Theme Style
lResult = pSetWindowTheme(hwnd, subApp, idlist);
}
}
}
::FreeLibrary(hinstDll);
}
return lResult;
}
}
void CListCtrl_ToolTip::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
// Focus retangle is not painted properly without double-buffering
#if (_WIN32_WINNT >= 0x501)
SetExtendedStyle(LVS_EX_DOUBLEBUFFER | GetExtendedStyle());
#endif
SetExtendedStyle(GetExtendedStyle() | LVS_EX_FULLROWSELECT);
SetExtendedStyle(GetExtendedStyle() | LVS_EX_HEADERDRAGDROP);
SetExtendedStyle(GetExtendedStyle() | LVS_EX_GRIDLINES);
// Enable Vista-look if possible
EnableWindowTheme(m_hWnd, L"ListView", L"Explorer", NULL);
}
三、CListCtrl添加提示类
//MyTooltipListCtrl.h
#pragma once
#include "afxcmn.h"
class CMyTooltipListCtrl : public CListCtrl
{
public:
CMyTooltipListCtrl();
~CMyTooltipListCtrl();
protected:
CToolTipCtrl m_toolTip; //文本提示类
int m_nSubItem; //存放行号
int m_nItem; //存放列号
BOOL m_bEnableTips; //是否开启文本提示
public:
virtual BOOL PreTranslateMessage(MSG* pMsg);
DECLARE_MESSAGE_MAP()
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
};
//MyTooltipListCtrl.cpp
#include "stdafx.h"
#include "MyTooltipListCtrl.h"
CMyTooltipListCtrl::CMyTooltipListCtrl()
{
m_bEnableTips = TRUE;
m_toolTip.Create(this, TTS_ALWAYSTIP);
m_toolTip.SetDelayTime(TTDT_AUTOPOP, 10000);//10 seconds
m_toolTip.SetMaxTipWidth(10);
}
CMyTooltipListCtrl::~CMyTooltipListCtrl()
{
}
BOOL CMyTooltipListCtrl::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
if (m_toolTip.GetSafeHwnd())
{
m_toolTip.RelayEvent(pMsg);
}
return CListCtrl::PreTranslateMessage(pMsg);
}
BEGIN_MESSAGE_MAP(CMyTooltipListCtrl, CListCtrl)
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
void CMyTooltipListCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//如果开启文本提示
if (m_bEnableTips)
{
CString str;
LVHITTESTINFO lvhti;
// 判断鼠标当前所在的位置(行, 列)
lvhti.pt = point;
SubItemHitTest(&lvhti);
//如果鼠标移动到另一个单元格内, 则进行处理; 否则, 不做处理
if ((lvhti.iItem != m_nItem) || (lvhti.iSubItem != m_nSubItem))
{
// 保存当前鼠标所在的(行,列)
m_nItem = lvhti.iItem;
m_nSubItem = lvhti.iSubItem;
// 如果鼠标移动到一个合法的单元格内,则显示新的提示信息
// 否则, 不显示提示
if ((m_nItem != -1) && (m_nSubItem != -1))
{
// 在这里修改要显示的提示信息
// 这里仅仅是一个例子---获得当前单元格的文字信息, 并设置为新的提示信息
str = GetItemText(m_nItem, m_nSubItem);
m_toolTip.AddTool(this, str);
// 显示提示框
m_toolTip.Pop();
}
else
{
m_toolTip.AddTool(this, "");
m_toolTip.Pop();
}
}
}
CListCtrl::OnMouseMove(nFlags, point);
}