Tooltips in modal dialog boxes

BOOL CToolTipDialog::OnInitDialog() { BOOL bResult = CDialog::OnInitDialog(); m_wndToolTip.Create(this); m_wndToolTip.Activate(c_bShowToolTips); CWnd *pWndChild = GetWindow(GW_CHILD); CString strToolTip; while (pWndChild) { int nID = pWndChild->GetDlgCtrlID(); if (strToolTip.LoadString(nID)) { m_wndToolTip.AddTool(pWndChild, strToolTip); } pWndChild = pWndChild->GetWindow(GW_HWNDNEXT); } return bResult; } The standard windows program has a main loop that calls GetMessage(), TranslateMessage() and DispatchMessage() continuously. The MFC main loop and the modal loops contain some extra's. One of these is calling PreTranslateMessage() before TranslateMessage(). This gives us the chance to peek at every message or to filter messages before they are dispatched. This is the place where we can intercept all mouse messages and relay them to the tooltip control.

BOOL CToolTipDialog::PreTranslateMessage(MSG *pMsg)
{
	if (c_bShowToolTips &&
		pMsg->message >= WM_MOUSEFIRST &&
		pMsg->message <= WM_MOUSELAST)
	{
		MSG msg;
		::CopyMemory(&msg, pMsg, sizeof(MSG));
		HWND hWndParent = ::GetParent(msg.hwnd);
		while (hWndParent && hWndParent != m_hWnd)
		{
			msg.hwnd = hWndParent;
			hWndParent = ::GetParent(hWndParent);
		}
		if (msg.hwnd)
		{
			m_wndToolTip.RelayEvent(&msg);
		}
	}
	return CDialog::PreTranslateMessage(pMsg);
}
As pointed out by Richard Collins, it is not enough to just relay the mouse events to the tooltip control. This would not work for controls that have child windwos. A combo box for example has an edit control as a child. When a mouse message is sent to this edit control, hWndParent will be the combo box, not the dialog. The message is adjusted to make the tooltip control believe it was actually sent to the combo box. Of course, the original message is passed on to the base class function.

Code Reuse

The CodeGuru site already contains an article by Dave Bixler that does just the same. However, Dave tells us to write the same code over and over again for every dialog. There are two reasons why you should avoid this:
  • My highschool math teacher used to say a mathematician should be as lazy as possible. This goes even more for a programmer. You should always write as few code as possible.
  • Chances are you will want to change this code later on. Maybe you'll want to display your tooltips as a marquee in an oval window. At that moment, you will want to change your code at exactly one place.

    Extending MFC

    This sample only contains a very small piece of code. You might wonder if it is worth the effort of making a general base class and deriving from it. However, you can put other stuff in this class later on, and all your dialogs will get the new stuff for free. This is a general principle that should be adhered to. When deriving a class from an MFC class, you should always create a middle class like this one. Everything that is general goes in the base class, everything that is application specific goes in the derived one. The result: When you are finished creating an application, you will have your own extension library to MFC. All functionality is there to be used in a new application.

    Putting strings in the resource file

    This may not be clear to Americans, but Europeans know all about it: you should put all your strings in a resource file. Microsoft has warned us often enough. Why? Because one day or another, people start asking if they can get a French, German,... version of your software. It might happen to you to, once your application is starting to get known all over the world.

    Making thing go automatically

    The spirit of MFC is making things go automatically. All kinds of terrible macros are invented to make the framework usable by wizards and all kinds of resources are loaded without you knowing it. As an example, tool tips for toolbars ae automatically loaded. This does not always enhance the readability of the MFC source, but it does make life easier for programmers. When extending MFC (or simply using it), you should follow the same principles. This is why the resource strings have the same ID as the controls. You should always try to find tricks like this. It can even sometimes be useful to create new macros that fit in the message maps or other maps.

    Download demo project - 18 KB

    Download source - 3 KB

    Last updated: January 25, 1999


  • Comments

    • tooltips

      Posted by Legacy on 05/14/2003 12:00am

      Originally posted by: jotham

      msdn of april 2000 has a sample program called drawcl
      how does one create tooltips for the polygons or shapes drawn

      Reply
    • Automatic Tooltips for any control

      Posted by Legacy on 05/14/2002 12:00am

      Originally posted by: Jaz

      This tip is great.  In addition to that, I have found the following to be useful for automating tooltips.  I placed this in my CDialogBase class which derives from CDialog and from which all Dialogs are based.  This automates all tool tips.
      
      

      If you add the following message handler, all controls which have text sent to the ::GetWindowText Function will be displayed in the tool tip.

      BEGIN_MESSAGE_MAP(CDialogBase, CDialog)
      //{{AFX_MSG_MAP(CDialogBase)
      ...
      //}}AFX_MSG_MAP
      ON_NOTIFY_EX( TTN_NEEDTEXT, 0, OnToolTipNotify )END_MESSAGE_MAP()

      BOOL CDialogBase::OnToolTipNotify(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
      {
      TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
      // idFrom is actually the HWND of the tool
      HWND hWnd =(HWND)pNMHDR->idFrom;
      if (pTTT->uFlags & TTF_IDISHWND)
      {
      // UINT nID = ::GetDlgCtrlID(hWnd);
      if(hWnd)
      {
      char cstrText[80];
      ::GetWindowText(hWnd, cstrText, 79);
      cstrText[79]='\0';
      pTTT->lpszText = cstrText;
      pTTT->hinst = AfxGetResourceHandle();
      return(TRUE);
      }
      }
      return(FALSE);
      }

      PS. This is a simplified version of what is presented by MS at
      http://support.microsoft.com/default.aspx?scid=kb;EN-US;q140595

      Reply
    • ComboBoxEx

      Posted by Legacy on 04/16/1999 12:00am

      Originally posted by: Philip Lee

      The latest code works with standard combo boxes but not combo box ex. Any ideas?

      Reply
    Leave a Comment
    • Your email address will not be published. All fields are required.

    Top White Papers and Webcasts

    • Specialization and efficiency are always in need. Whether it's replacing an aging roof, getting a haircut, or tuning up a car, most seek the assistance of trusted experts. The same is true in the business world, where an increasing number of companies are seeking the help of others to administer their IT systems and services. This special edition of Unleashing IT highlights a new breed of IT caretaker -- Cisco Powered service providers -- and the business advantages and operational efficiencies they …

    • As everyone scrambles to protect customers and consumers from the Heartbleed virus, there will be a variety of mitigating solutions offered up to address this pesky bug. There are a variety of points within the data path where solutions could be put into place to mitigate this (and similar) vulnerabilities and customers must choose the most strategic point in the network at which to deploy their selected mitigation. Read this white paper to learn the ins and outs of mitigating the risk of Heartbleed and the …

    Most Popular Programming Stories

    More for Developers

    Latest Developer Headlines

    RSS Feeds