Word-style Tooltips for Scrollbars






4.75/5 (11 votes)
Jul 6, 2004
6 min read

72542

1253
You've seen the tooltip windows that Word displays when you drag the scrollbar. Now you can add them to your own applications.
Introduction
Microsoft Word displays a tooltip when you drag the vertical scrollbar in a document. The tooltip shows the number of the page that will be displayed when you stop dragging, and sometimes other information as well. This article shows how this can be done.
Background
I've been developing an MFC application to play Midi files and show music scores. The score window takes too long to redraw as the scrollbar is being dragged, so the program waits until dragging has stopped before redrawing the window. I wanted some feedback on exactly where the scrollbar was as I was dragging it, so that I could stop dragging at the required bar number. What was needed was a tooltip window, very much as Microsoft Word displays the page number in a tooltip as the vertical scrollbar is being dragged.
MFC has a class called CToolTipCtrl
, so that seemed the obvious place to start. But
it seems to have a built-in delay, and
I couldn't work out how the make the tooltip window appear without delay
when I clicked the mouse on the scrollbar.
Fortunately, I then found this article on CodeProject (thanks Zarembo!), which displays a tooltip window without any delay. I converted the code to C++, wrapped it in a class, tweaked it bit, and it works fine.
Adding a tooltip to a scrollbar
You can add a tooltip to the vertical or the horizontal scrollbar in a window, or both. The following describes how to add a tooltip to the vertical scrollbar:- Add the following files to your project:
- CRHTipWnd.cpp
- CRHTipWnd.h
- In the .h file for your window class, add the following line:
#include "CRHTipWnd.h"
and in the class itself add:CRHTipWnd m_CRHTipWnd; // make this private or protected as you prefer
- Add a handler for the
WM_VSCROLL
Windows message to your window class: To do this, right-click in the ClassView window on your window class, choose "Add Windows Message Handler...". Click onWM_VSCROLL
in the "New Windows messages/events:", list then click on the "Add Handler" button. Click on "OK" to finish. - In the
OnVScroll()
routine that you've just created, add the following lines:switch (nSBCode) { case SB_THUMBTRACK: m_CRHTipWnd.CRHInit("Line: 99999"); // ) Or whatever strings and values you want. m_CRHTipWnd.CRHSetText("Line: %i", nPos + 1); // ) See below for explanation. break; case SB_THUMBPOSITION: m_CRHTipWnd.CRHClear(); break; }
CRHTipWnd
class.
If you build and run your program you should see some sort of tooltip window appearing when
you drag the vertical scrollbar in the window whose class you've modified.
If you want the
tooltip to apply to the horizontal scrollbar instead of the vertical scrollbar then add a
handler for WM_HSCROLL
instead of WM_VSCROLL
.
If you want tooltips for both scrollbars then you'll need two CRHTipWnd
objects in the class,
you must add handlers for WM_VSCROLL
and WM_HSCROLL
, and the two handlers must refer to the
two different CRHTipWnd
objects.
The demo program
This is the MULTIPAD MFC sample program, with the following changes to demonstrate a tooltip window appearing when the vertical scrollbar is dragged:CRHTipWnd
class added to the project.- New
CRHEditView
class added, derived fromCEditView
. This is so that I could make the changes to the window class as described above. CEditView
changed toCRHEditView
in the following line:((CRHEditView*)m_viewList.GetHead())->SerializeRaw(ar);
inCPadDoc::Serialize()
. This is so that the code inCRHEditView
gets called.
Using the CRHTipWnd
class
This constructs a
CRHTipWnd::CRHTipWnd()
CRHTipWnd
object. The Windows tooltip window is not created
and attached until CRHTipWnd::CRHInit()
is called.
This creates a Windows tooltip window and attaches it to the
CRHTipWnd::CRHInit(const char *BigString)
CRHTipWnd
object.
It does no harm to call CRHTipWnd::CRHInit()
more than once. However, only the first call
to CRHTipWnd::CRHInit()
after CRHTipWnd::CRHTipWnd()
or CRHTipWnd::CRHClear()
is called has any effect.
This provides better encapsulation of CRHTipWnd
by freeing
the code calling CRHTipWnd::CRHInit()
from having to keep track of whether it's
called it already.
The const char *BigString
parameter tells CRHTipWnd
how big to make the tooltip window.
The tooltip window is a fixed size, determined by BigString
.
This gets round a minor problem I hit when developing this code, which is
described in "Points of Interest" below.
This sets the text in the tooltip window. It takes a variable number of parameters in the same sort of way as void
void CRHTipWnd::CRHSetText(const char *fmt, ...)
CString::Format()
.
This destroys the Windows tooltip window attached to the
CRHTipWnd::CRHClear()
CRHTipWnd
object.
After CRHTipWnd::CRHClear()
has been called, CRHTipWnd::CRHInit()
may be called again,
to create another Windows tooltip window, possibly in a different place and with a different size.
Typically, you would call CRHTipWnd::CRHInit()
and CRHTipWnd::CRHSetText()
to handle an
SB_THUMBTRACK
event in your OnHScroll()
or OnVScroll()
routine (or both),
and CRHTipWnd::CRHClear()
to handle an SB_THUMBPOSITION
event.
How the CRHTipWnd
class works
You can just leave the CRHTipWnd
class to do its job, or you can take a peek inside.
The key to creating a tooltip window is to call CWnd::CreateEx()
using the registered class name
TOOLTIPS_CLASS
(which is #define
d in COMMCTRL.H to be "tooltips_class32"
).
I'm not sure which of the other parameters are critical, but the following seems to work:
CreateEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL);This is done in
CRHTipWnd::CRHInit()
. Once the tooltip window has been created, CRHTipWnd
sends various TTM_
messages to it to make it appear, display the right text, and disappear as required.
Points of Interest
- There's nothing in the
CRHTipWnd
class that restricts its use to scrollbars. You might find it useful for other situations where you want to display a tooltip window with varying text in it for a period of time.
- I hit a minor problem when developing the code, which was as follows:
A tooltip window is normally just big enough to contain the text. When the width of the
text gets smaller the tooltip window normally gets narrower. This causes Windows to
repaint the tiny piece of the window that's now appeared from behind the tooltip window. In
my music program this was sometimes taking a few seconds.
The solution was to prevent the tooltip window from being resized after its initial size is set.CRHTipWnd::OnWindowPosChanging()
looks after this by doing:if (CRHInitCalled) lpwndpos->flags |= SWP_NOSIZE;
However, you don't need to worry about this just to useCRHTipWnd
.
- In order that the tooltip window
doesn't get hidden by the mouse cursor when a vertical or a
horizontal scrollbar is being dragged,
the window is drawn so that its bottom right corner is where the mouse
cursor was pointing (at least, it's supposed to be, in practice it always seems
to be a few pixels to the left of that).
- If anyone's interested in seeing the original MidiPlay application that prompted this article, you can find it here. There are instructions there on how to download and use it. To display the score window with the scrollbar tooltip, open a .MID file (for example this one), double click on one of the track names, drag the scrollbar and there's the tooltip!
History
- 5th July 2004 - first submitted.