Click here to Skip to main content
Click here to Skip to main content

Neat Tooltip for ComboBox

, 30 Jul 2012
Rate this:
Please Sign up or sign in to vote.
Details of how to make a tool-tip for a combobox. The enhanced class CTTCombobox is derived from the MFC ComboBox class.

Introduction

I have always been perplexed by the question "which's the tool-tip associated with ComboBox?". On the internet, I searched a lot for samples about this, but the samples looked so complex, and some of the samples wrapped the ComboBox using separate controls CListbox, CEdit, CButton, requiring to generate many extra classes for the ComboBox, resulting in lots of work to maintain the ComboBox. A couple of samples used hooks for window subclassing so that all the messages go to a new WindowProc (window procedure) before going to the ComboBox, ummm..., that's very useful. But that is still not enough. Thus, based on these samples, I wrapped an enhanced class CTTCombobox. According to the expectation, they use the new WindowProc to hold the ListBox's messages and to do the tool-tip things. By the way, for the enhanced class CTTCombobox, the source files contain only TTCombobox.h and TTCombobox.cpp, which is very easy to embed into your VC++ project.

Noteworthy

The class CTTComboBox isn't wrapped using separate controls CListbox, CEdit, CButton.

To use

  1. The class CTTComboBox is derived from an MFC class CCombobox. You can immediately use the class CTTComboBox as well as derive new classes from it.
  2. By default, tool-tip is allowed for the ComboBox (instance of the class CTTComboBox); to enable/disable the tool-tip, you need to perform the SetToolTipp function before creating or subclassing the ComboBox; for details, see the attached demo project.
  3. Use the SetToolTipDelay function to set up the appropriate delay time for showing the tool-tip window.

Implementation

As mentioned above, by default, tool-tip is allowed, means m_isEnableTool is TRUE; see the code sample given below:

void  CTTComboBox::PreSubclassWindow() 
{
 CComboBox::PreSubclassWindow();

 // If the combo-box isn't dynamically 
 // subclassed that should get rid of 
 // the codes as follows and the codes should be 
 // placed in the OnCreate function.
 if (m_isEnableTool == TRUE)
 {
  CreateTooltipWnd();
  InstallHookForListboxAndEditbox();
 }
}

We have called the CreateTooltipWnd function to create a tool-tip window. (Note: all the ComboBoxes with the same class CTTComboBox share a tool-tip window.) Now, we have to install a hook for subclassing the list-box and the edit-box portion of the ComboBox, see the InstallHookForListboxAndEditbox function given below:

void CTTComboBox::InstallHookForListboxAndEditbox()
{
 // Get the list-box/edit-box within the combobox 
 // and set up hooking with it.
 // Note: this only for Win98 or WinNT5.0 or later, 
 // However you must defined WINVER >= 0x0500 
 // within the project
 COMBOBOXINFO cbi;
 ZeroMemory(&cbi, sizeof(COMBOBOXINFO));
 cbi.cbSize = sizeof(COMBOBOXINFO);
 ::GetComboBoxInfo(m_hWnd, &cbi);
 m_rcButton = cbi.rcButton;

 if (cbi.hwndList)
 {
  m_hWndList = cbi.hwndList;

  // Set up hooking for the list-box within the combobox
  // Note: Refer to MSDN, In Windows NT/2000, You cannot 
  // change this attribute(GWL_WNDPROC) 
  // if the window(i.e, list-box) does not belong to the 
  // same process as the calling thread.
  WNDPROC oldListWndProc = (WNDPROC)::SetWindowLong(cbi.hwndList, 
                         GWL_WNDPROC, (DWORD)HookListboxWndProc);
  // Add the old hook(i.e, hook associated 
  // with window) to the map
  m_mapWndProc.SetAt(cbi.hwndList, oldListWndProc);
 }
 
 if (cbi.hwndItem)
 {
  m_hWndEdit = cbi.hwndItem;

  // Set up hooking for the edit-box within the combobox
  // Note: Refer to MSDN, In Windows NT/2000, You cannot 
  // change this attribute(GWL_WNDPROC) 
  // if the window(i.e, edit-box) does not belong to the 
  // same process as the calling thread.
  WNDPROC oldEditWndProc = (WNDPROC)::SetWindowLong(cbi.hwndItem, 
                         GWL_WNDPROC, (DWORD)HookEditboxWndProc);
  // Add the old hook(i.e, hook associated 
  // with window) to the map
  m_mapWndProc.SetAt(cbi.hwndItem, oldEditWndProc);
 }
}

The GetComboBoxInfo function gets the handles to the list-box and the edit-box, then the SetWindowLong function creates the list-box and the edit-box subclasses, causing the system to call the new window procedure instead of the previous one, a trick, that's all previous (old) window procedures have been added to the map. Later, we will reuse the old window procedure in the new window procedure, such as the window procedure - HookListboxWndProc with the list-box:

LRESULT CALLBACK CTTComboBox::HookListboxWndProc(HWND hWnd, 
        UINT message, WPARAM wParam, LPARAM lParam)
{
 CListBox* pList = (CListBox*)CWnd::FromHandle(hWnd);

 if (message == WM_MOUSEMOVE)
 {
  WORD xPos, yPos;
  xPos = LOWORD(lParam);
  yPos = HIWORD(lParam);
  CPoint point(xPos, yPos); 
  CRect rcClient;
  ::GetClientRect(hWnd, &rcClient);
  if (rcClient.PtInRect(point))
  {
   // Handle mouse move event that may 
   // show a tool-tip window...
   CTTComboBox::HandleListboxMouseMove(pList, 
                                 wParam, point);
  }

  if (!m_isEnter)
  {
   // Tracking the mouse event which 
   // are hovering and leaving.
   OnTrackMouseEvent(hWnd, TME_HOVER|TME_LEAVE);
   m_isEnter = TRUE;
  }
 }
 else if (message == WM_MOUSELEAVE)
 {
  // When the mouse cursor has been 
  // left current window, 
  // the original select of the list-box 
  // to reset LB_ERR.
  m_OriginalSel = LB_ERR;
  m_isEnter = FALSE;
 }
 else if (message == WM_CAPTURECHANGED)
 { // To omit the mouse capture changed...
  return 1;
 }

 // Get previous window procedure
 WNDPROC oldListWndProc;
 m_mapWndProc.Lookup(hWnd, oldListWndProc);
 // Call previous window procedure
 return ::CallWindowProc(oldListWndProc, 
            hWnd, message, wParam, lParam);
}

At the end of the function, we reuse the old window procedure by calling the CallWindowProc function from the map like a chain of window procedures. Some of the messages have been reprocessed for the list-box. While processing the WM_MOUSEMOVE, the HandleListboxMouseMove function determines whether or not the tool-tip window has to be shown/hidden, and the OnTrackMouseEvent function tracks the mouse event messages WM_MOUSEHOVER or WM_MOUSELEAVE, that reset the original selection of the list-box when firing up WM_MOUSELEAVE. Waiting..., the WM_MOUSELEAVE message takes a continuous place, but the mouse cursor will still be above the list-box, actually, the mouse cursor will be above the tool-tip window when the tool-tip window shows up, think over? So we must capture the list-box to prevent this, to release the mouse capture only when the tool-tip has been hidden.

The ComboBox with owner DropList or DropDown style has a little trouble: when the mouse capture is released, the list-box becomes invisible. So, I have to hold the WM_CAPTURECHANGED and ignore the message...

Usage

You are allowed to use it for free and further modify it according to your needs.

Updates

  • 27th Sep, 2006.
  • There are two extra features for the class CTTCombobox, it now supports these features:

    • Allows that you align the listbox by the three modes CTTCombobox::alignLeft, CTTCombobox::alignRight, CTTCombobox::alignMiddle, using the function SetAlignStyle to do this thing.
    • To disable or enable any items in a combobox, you only simply perform the function SetDisabledItem. Note: that's not effective when the combobox does not have the CBS_OWNERDRAWFIXED style, so I suggest you should set the style. There are three methods to gain the style CBS_OWNERDRAWFIXED.
      1. You directly check the style for the combobox in the resource.
      2. If the combobox was created dynamically, then you should do the thing in OnCreate(LPCREATESTRUCT lpCreateStruct). Usually, you write the code lpCreateStruct->style |= CBS_OWNERDRAWFIXED | CBS_HASSTRINGS; as the first line in the function OnCreate(LPCREATESTRUCT lpCreateStruct).
      3. If the combobox was subclassed dynamically, then you add the code: ModifyStyle(0, CBS_OWNERDRAWFIXED | CBS_HASSTRINGS); as the first line in PreSubclassWindow().
  • 8th June, 2005.
  • Thanks d3m0n, for finding out some bugs in the control. I have fixed all the bugs as follows:

    • Hover over the ComboBox and the tool tip appears. Then while the tool tip is still visible, click the arrow to expand the ComboBox. The dropdown list appears and then disappears immediately.
    • While the tool tip is visible, nothing can receive the focus. E.g., try clicking the Cancel button while the tool tip is visible.
    • The size of the tool tip is sometimes too big, e.g., hover over the ListBox, wait for the tool tip to appear and disappear, now move the mouse out of the ListBox. Now move it into the edit box. The size of the tool tip is really too big.
  • 21st July, 2011.
  • The updated source and demo projects now support the X64 platform and Unicode version compilation for all platforms (Win32 and X64), and a few minor issues have been fixed. The demo project gives two project solutions respectively for VC6.0 and VS2005. Naturally, you can upgrade the solution with VS2008/VS2010.

  • 19th November 2011
  • There is a new function - SetTipText(int nIndex, LPCTSTR pszTipText). The function gives a customized method to setup tip text for a specified item. Note: this will force the display of the tip text, and if the customized tip text is no longer needed for that item, you're responsible to call the function again and pass pszTipText=NULL.

License

This article, along with any associated source code and files, is licensed under The BSD License

Share

About the Author

simonchen.net
Software Developer (Senior) www.likefreelancer.com
China China
I was borned at Jianghan district, Qianjiang Hubei province of China, and always pursuiting his dream in Beijing.
 
Last century 1996, I got a learning machine called Subor(1.8MHZ CPU, 2k memory) that it can be connected to TV looked like computer emulater, the machine is really good for learning G-Basic, and Wubi inputs, after soon, A real PC was presented to me, the core was 586 pentium MMX, really graceful! in that mean-time, I practiced Javascript language and dynamic HTML on that PC, of course, including many famouse classic games. also, I have further self-studied professional electronic courses via Wuhan University.
 
A great starting point was in 2001, I went to a software firm and did deepth experience in real software development, learned C/C++ and ASP(VBscript).
 
In 2005, I moved to another creative firm, major responsibility is to develop windows GUI based on MFC framework, and early touched Mac PowerPC and Macbook for migrating soft. I'm still there and developing a great social web application.
 
In 2007, I have married with my wife and had a very cute daughter she's close to 3 old now. At all available time, I has been activating on freelancer.com and doing lots of amazing jobs.

Comments and Discussions

 
QuestionGood Job, just a couple of suggestions [modified] PinmemberGuglielmo Calligaro14-Feb-13 0:05 
GeneralMy vote of 4 PinmemberVictor Nijegorodov5-Aug-12 23:39 
GeneralRe: My vote of 4 Pinmembersimonchen.net7-Aug-12 23:31 
GeneralMy vote of 5 Pinmemberonnlv11-Jun-12 19:22 
GeneralRe: My vote of 5 Pinmembersimonchen.net30-Jul-12 2:24 
QuestionTooltips and Combo Items different? Pinmembersymreds17-Oct-11 3:51 
AnswerRe: Tooltips and Combo Items different? Pinmembersimonchen.net20-Nov-11 2:17 
General64 bit machine Pinmemberli123427-Oct-09 18:36 
GeneralRe: 64 bit machine PinmemberXinyiChen23-May-11 23:27 
GeneralGetting error with COMBOBOXINFO . included "winuser.h" but problem still persist PinmemberElsie17-Sep-09 5:55 
GeneralRe: Getting error with COMBOBOXINFO . included "winuser.h" but problem still persist PinmemberElsie17-Sep-09 6:10 
GeneralNo tooltips for DropList PingroupGuido van den Berg23-Jun-09 4:27 
GeneralRe: No tooltips for DropList Pinmemberstarschen22-Jul-09 17:34 
GeneralIssue in the Latest Windows OS PinmemberMd Saleem Navalur20-May-09 4:47 
GeneralRe: Issue in the Latest Windows OS [modified] PinmemberMd Saleem Navalur26-May-09 0:00 
GeneralRe: Issue in the Latest Windows OS Pinmemberstarschen22-Jul-09 17:43 
GeneralRe: Issue in the Latest Windows OS Pinmemberranxiaowu4-Jan-10 21:18 
GeneralBinary or C#/VB.NET version Pinmembermanishkjain16-Jan-08 8:34 
QuestionHow to create this CTTComboBox dynamically? PinmemberKylixLee8-Nov-06 21:15 
AnswerRe: How to create this CTTComboBox dynamically? Pinmemberstarschen11-Nov-06 3:08 
Generalc++, crazzzy people Pinmemberjswoofer27-Sep-06 9:42 
GeneralRe: c++, crazzzy people Pinmemberstarschen27-Sep-06 15:04 
QuestionCTTComboBox vs CListbox PinmemberIvar7418-Sep-06 4:25 
AnswerRe: CTTComboBox vs CListbox Pinmemberstarschen27-Sep-06 14:50 
GeneralMine crashes too Pinmemberfretjel1-Jun-06 22:19 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140814.1 | Last Updated 30 Jul 2012
Article Copyright 2005 by simonchen.net
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid