How to Use CMFCToolTipCtrl Tooltips in a View for Dynamic Context Sensitive Tips
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
/////////////////////////////////////////////////////////////////////////////
// CCustomToolTipCtrl window
class CCustomToolTipCtrl : public CMFCToolTipCtrl
{
// Construction
public:
CCustomToolTipCtrl();
// Attributes
public:
int m_nCurrID;
// Operations
public:
// Overrides
// Implementation
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::CCustomToolTipCtrl()
{
m_nCurrID = 0;
}
CCustomToolTipCtrl::~CCustomToolTipCtrl()
{
}
/////////////////////////////////////////////////////////////////////////////
// CCustomToolTipCtrl message handlers
CSize CCustomToolTipCtrl::OnDrawLabel(CDC* pDC, CRect rect, BOOL bCalcOnly)
{
// TRACE("ENTER CCustomToolTipCtrl::OnDrawLabel bCalcOnly=%d\n", bCalcOnly);
ASSERT_VALID(pDC);
// my special tooltip drawing system for context sensitive tooltips
// in view (track)
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) // Multi-line text
{
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.
//
// my special tooltip drawing system for context sensitive tooltips in view (track)
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:
// Create the ToolTip control.
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); // 3 seconds before tooltips pop up in view
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
Post Comment
jtW4Ub You have mentioned very interesting details ! ps decent internet site. I didn at attend the funeral, but I sent a nice letter saying that I approved of it. by Mark Twain.
SXg5Zo My brother suggested I might like this web site. He was entirely right. This post actually made my day. You can not imagine just how much time I had spent for this information! Thanks!
wmGb5p Looking forward to reading more. Great article post.Thanks Again. Cool.
2q9JVV I really liked your post.Thanks Again. Much obliged.
k9cYSE wow, awesome article. Want more.
T8WEzz This is one awesome post.Much thanks again. Cool.
These makes and the season, all on potential and feel positive. Stay be these types to.