Introduction
CMFCToolTipCtrl
provides a very nice looking, multi-line tooltip for use on controls such as buttons, sliders, etc.
I also wanted to use the same class to display dynamic tooltips based on the location of the mouse in the view area (CSrollView
class in my case).
This article will describe a very simple way to override the OnDrawLabel
method to get the tooltip to draw any text you like at run time.
Background
I have used a tooltip class from this site before but my solution is very simple and uses the nicer looking CMFCToolTipCtrl
class.
This code is based on the DlgToolTips
example that comes with VS 2010.
Using the Code
Here is the class header CCustomToolTipCtrl.h.
The key thing is the override of OnDrawLabel
.
#pragma once
class CCustomToolTipCtrl : public CMFCToolTipCtrl
{
public:
CCustomToolTipCtrl();
public:
int m_nCurrID;
public:
public:
virtual ~CCustomToolTipCtrl();
protected:
CSize OnDrawLabel(CDC* pDC,CRect rect,BOOL bCalcOnly);
};
Here is the class declaration CCustomToolTipCtrl.cpp.
Most of the code is taken directly from the original implementation of CMFCToolTipCtrl::OnDrawLabel
in MFC. The interesting part is how I produce the two string
s, labelText
and descriptionText
.
#include "stdafx.h"
#include "CustomToolTipCtrl.h"
CCustomToolTipCtrl::CCustomToolTipCtrl()
{
m_nCurrID = 0;
}
CCustomToolTipCtrl::~CCustomToolTipCtrl()
{
}
CSize CCustomToolTipCtrl::OnDrawLabel(CDC* pDC, CRect rect, BOOL bCalcOnly)
{
ASSERT_VALID(pDC);
POINT cursor;
GetCursorPos(&cursor);
GetActiveView()->ScreenToClient(&cursor);
CString labelText, descriptionText;
GetActiveView()->GetToolTipLabelText(cursor, labelText, descriptionText);
CSize sizeText(0, 0);
BOOL bDrawDescr = m_Params.m_bDrawDescription && !m_strDescription.IsEmpty();
CFont* pOldFont = (CFont*) pDC->SelectObject(m_Params.m_bBoldLabel &&
bDrawDescr ? &afxGlobalData.fontBold : &afxGlobalData.fontTooltip);
if (labelText.Find(_T('\n')) >= 0) {
UINT nFormat = DT_NOPREFIX;
if (bCalcOnly)
{
nFormat |= DT_CALCRECT;
}
if (m_pRibbonButton != NULL)
{
nFormat |= DT_NOPREFIX;
}
int nHeight = pDC->DrawText(labelText, rect, nFormat);
sizeText = CSize(rect.Width(), nHeight);
}
else
{
if (bCalcOnly)
{
sizeText = pDC->GetTextExtent(labelText);
}
else
{
UINT nFormat = DT_LEFT | DT_NOCLIP | DT_SINGLELINE;
if (!bDrawDescr)
{
nFormat |= DT_VCENTER;
}
if (m_pRibbonButton != NULL)
{
nFormat |= DT_NOPREFIX;
}
sizeText.cy = pDC->DrawText(labelText, rect, nFormat);
sizeText.cx = rect.Width();
}
}
pDC->SelectObject(pOldFont);
SetDescription (descriptionText);
return sizeText;
}
The code below gets the current absolute cursor position, then converts it to a point relative to the current view.
Then GetToolTipLabelText
fills in the two cstring
s with text based on the location of the cursor in the view window.
POINT cursor;
GetCursorPos(&cursor);
GetActiveView()->ScreenToClient(&cursor);
CString labelText, descriptionText;
GetActiveView()->GetToolTipLabelText(cursor, labelText, descriptionText);
This line draws the text for the top part of the tooltip:
pDC->DrawText(labelText, rect, nFormat)
This line draw the text for the bottom part of the tooltip:
SetDescription (descriptionText);
Now, how to hook it up to your application. In the view's constructor:
m_ToolTip.Create(this);
m_ToolTip.Activate(TRUE);
m_ToolTip
is of type CCustomToolTipCtrl
:
In the view's OnInitialUpdate
:
CMFCToolTipInfo params;
params.m_bVislManagerTheme = TRUE;
m_ToolTip.SetParams (¶ms);
m_ToolTip.AddTool (this, _T("BASE LABLE"));
m_ToolTip.SetDelayTime(3000);
You will need a PreTranslateMessage
to force events to get routed to the tooptip
class (this is the critical bit of the whole thing):
BOOL CTabTraxView::PreTranslateMessage(MSG* pMsg)
{
switch (pMsg->message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
m_ToolTip.RelayEvent(pMsg);
break;
}
return CScrollView::PreTranslateMessage(pMsg);
}
Finally, at the end of my view's OnMouseMove
, I need to get rid of the tooltip (not sure why it doesn't do it automatically but this works).
m_ToolTip.Pop();
And that's about it, less than 100 lines of code and it uses the built in features in MFC.
Points of Interest
I should point out that I'm not really an expert in MFC programming. I just figured this out by debugging the DlgToolTip
app so I can't guarantee that this is the best or safest way to do it, but for me it seems to work fine.
History
- 28th May, 2011: Initial version
- 9th June, 2011: Uploaded demo project