Click here to Skip to main content
15,884,473 members
Articles / Desktop Programming / MFC
Article

Word-style Tooltips for Scrollbars

Rate me:
Please Sign up or sign in to vote.
4.75/5 (11 votes)
5 Jul 20046 min read 71.8K   1.3K   29   8
You've seen the tooltip windows that Word displays when you drag the scrollbar. Now you can add them to your own applications.

Image 1

Image 2

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 on WM_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;
    }
    
That's all you have to do. The rest of the work is done by the 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 from CEditView. This is so that I could make the changes to the window class as described above.
  • CEditView changed to CRHEditView in the following line:
    ((CRHEditView*)m_viewList.GetHead())->SerializeRaw(ar);
    
    in CPadDoc::Serialize(). This is so that the code in CRHEditView gets called.

Using the CRHTipWnd class

<br>CRHTipWnd::CRHTipWnd()<br><br>
This constructs a CRHTipWnd object. The Windows tooltip window is not created and attached until CRHTipWnd::CRHInit() is called.

<br>CRHTipWnd::CRHInit(const char *BigString)<br><br>
This creates a Windows tooltip window and attaches it to the 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.

<br>void CRHTipWnd::CRHSetText(const char *fmt, ...)<br><br>
This sets the text in the tooltip window. It takes a variable number of parameters in the same sort of way as void CString::Format().

<br>CRHTipWnd::CRHClear()<br><br>
This destroys the Windows tooltip window attached to the 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 #defined 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 use CRHTipWnd.

  • 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 <a href="http://mysite.freeserve.com/staplefordsingers/members.html#midiplay" target="_blank">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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Retired
United Kingdom United Kingdom
I've been programming computers since about 1968. I started at school with Algol 60 on an Elliott 803. From there I progressed through the Z80 and other microprocessors to the PC, DOS and Windows, Pascal, C and C++.

My other interests include astronomy and classical music. All of my contributions to Code Project have arisen from programs I've written in these areas.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Farhan Ghumra17-Jun-12 23:36
professionalFarhan Ghumra17-Jun-12 23:36 
GeneralC# or VB.net Pin
Anthony Daly18-Aug-09 23:47
Anthony Daly18-Aug-09 23:47 
AnswerRe: C# or VB.net Pin
Chris Hills19-Aug-09 7:06
Chris Hills19-Aug-09 7:06 
GeneralRe: C# or VB.net Pin
Anthony Daly19-Aug-09 7:10
Anthony Daly19-Aug-09 7:10 
Questionis behavior like outlook possible? Pin
Dieter Hammer6-Jul-04 5:12
Dieter Hammer6-Jul-04 5:12 
AnswerRe: is behavior like outlook possible? Pin
Chris Hills6-Jul-04 8:57
Chris Hills6-Jul-04 8:57 
QuestionCould you explain? Pin
WREY5-Jul-04 23:27
WREY5-Jul-04 23:27 
AnswerRe: Could you explain? Pin
Chris Hills6-Jul-04 2:33
Chris Hills6-Jul-04 2:33 
CMainFrame::OnCreate() comes straight from the MULTIPAD MFC sample program. The only line I've changed in multipad.cpp is ((CRHEditView*)m_viewList.GetHead())->SerializeRaw(ar);, as I mentioned. The ternary condition does seem to be rather unnecessary.

I'm interested that you see the 99999. I was half expecting this, but it didn't happen to me. You can stop it by replacing the "Line: 99999" string with "Line:      ". I used 99999 to make sure the tooltip window was wide enough for any 5-digit number.

Cheers,
Chris.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.