Figure 1   CWnd Support for ToolTips

Member
Description
BOOL EnableToolTips(BOOL bEnable)
Enables or disables ToolTips for a window
virtual int CWnd::OnToolHitTest(CPoint point, TOOLINFO* pTI ) const
Called by the framework to determine if the cursor is over a tool that has a ToolTip
void FilterToolTipMessage(MSG* pMsg)
Checks to see if a message is relevant for displaying ToolTips
static void PASCAL CancelToolTips(BOOL bKeys)
Hides a ToolTip if one is currently displayed

Figure 4   HTML for a ToolTip

ASP File:


 <OBJECT ID="WebButton1" WIDTH=31 HEIGHT=28
  CLASSID="CLSID:381C5023-2FDA-11D0-8BC1-444553540000">
     <PARAM NAME="_Version" VALUE="65536">
     <PARAM NAME="_ExtentX" VALUE="786">
     <PARAM NAME="_ExtentY" VALUE="731">
     <PARAM NAME="_StockProps" VALUE="0">
     <PARAM NAME="ToolTipText" VALUE="WebButton ToolTip Test">
 </OBJECT>
 </P>
 
 <br><img  src="Image.gif"  height=48 width=48 alt="Image ToolTip" >
 
 <SCRIPT LANGUAGE="VBScript">
 <!--
 Sub WebButton1_Click()
 MsgBox "WebButton was clicked"
 end sub
 -->
     </SCRIPT>

Figure 5   CWebButtonCtrl


 // WebButtonCtl.cpp : Implementation of the CWebButtonCtrl OLE control class.
 ?
 ?
 ?
 /////////////////////////////////////////////////////////////////////////////
 // CWebButtonCtrl::RelayToolTipEvent - Pass mouse messages to ToolTip
 
 void CWebButtonCtrl::RelayToolTipEvent(const MSG* pMsg)
 {
     MSG MsgCopy;
     ::memcpy(&MsgCopy, pMsg, sizeof(MSG));
     FilterToolTipMessage(&MsgCopy);
 }
 
 
 int CWebButtonCtrl::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
 {
     if (m_bToolTipEnabled && pTI != NULL && pTI->cbSize >= sizeof(TOOLINFO))
     {
         // setup the TOOLINFO structure
         pTI->hwnd = m_hWnd;
         pTI->uId = 0;
         pTI->uFlags = 0;
         GetClientRect(&(pTI->rect));
         pTI->lpszText = LPSTR_TEXTCALLBACK;
     }
 
     return (m_bToolTipEnabled ? 1 : -1);
 }
 
 
 /////////////////////////////////////////////////////////////////////////////
 // CWebButtonCtrl message handlers
 
 int CWebButtonCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
 {
     if (COleControl::OnCreate(lpCreateStruct) == -1)
     {
         return -1;
     }
     
     if (m_Bitmap.LoadBitmap(IDB_WEBBUTTON))
     {
         SendMessage(BM_SETIMAGE, IMAGE_BITMAP, 
             (LPARAM)m_Bitmap.GetSafeHandle());
     }
     else
     {
         TRACE("Unable to load bitmap for button.");
     }
 
     EnableToolTips(TRUE);
     return 0;
 }
 
 void CWebButtonCtrl::OnMouseMove(UINT nFlags, CPoint point) 
 {
     RelayToolTipEvent(GetCurrentMessage());
     COleControl::OnMouseMove(nFlags, point);
 }
 
 void CWebButtonCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
 {
     RelayToolTipEvent(GetCurrentMessage());
     COleControl::OnLButtonDown(nFlags, point);
 }
 
 void CWebButtonCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
 {
     RelayToolTipEvent(GetCurrentMessage());
     COleControl::OnLButtonUp(nFlags, point);
 }
 
 BOOL CWebButtonCtrl::OnToolNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult)
 {
     TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
     ::strcpy(pTTT->szText, m_strToolTipText);
     return TRUE;
 }
 
 
  
 /////////////////////////////////////////////////////////////////////////////
 // Property changed handlers
 
 void CWebButtonCtrl::OnToolTipEnabledChanged() 
 {
     SetModifiedFlag();
 }
 
 void CWebButtonCtrl::OnToolTipTextChanged() 
 {
     SetModifiedFlag();
 }

Figure 7   CCircle Hit-testing


 /////////////////////////////////////////////////////////////////////////////
 // CCircle hittesting
 
 inline double Square(int n) { return (double(n) * double(n)); }
 
 BOOL CCircle::HitTest(const CPoint& Point) const
 {
     CPoint Diff = m_CenterPoint - Point;
     return ((Square(Diff.x) + Square(Diff.y)) <= Square(m_nRadius));
 }

Figure 8   DTDemoView


 /////////////////////////////////////////////////////////////////////////////
 // DTDemoView.cpp : implementation of the CDTDemoView class
 ?
 ?
 ?
 /////////////////////////////////////////////////////////////////////////////
 // CDTDemoView HitTest
 
 const CCircle* CDTDemoView::HitTest(const CPoint& Point)
 {
     CDTDemoDoc* pDoc = GetDocument();
     ASSERT_VALID(pDoc);
 
     // Check in reverse order to deal with clipping.
     const CCircle *pCircleHit = NULL;
     for (int n = pDoc->GetCircleCount() - 1; n >= 0 && pCircleHit == NULL; n--)
     {
         if (pDoc->GetCircle(n).HitTest(Point))
         {
             pCircleHit = &(pDoc->GetCircle(n));
         }
     }
     return pCircleHit;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // CDTDemoView drawing
 
 void CDTDemoView::OnDraw(CDC* pDC)
 {
     CDTDemoDoc* pDoc = GetDocument();
     ASSERT_VALID(pDoc);
 
     for (int n = 0; n < pDoc->GetCircleCount(); n++)
     {
         pDoc->GetCircle(n).Draw(pDC);
     }
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // CDTDemoView diagnostics
 
 #ifdef _DEBUG
 void CDTDemoView::AssertValid() const
 {
     CView::AssertValid();
 }
 void CDTDemoView::Dump(CDumpContext& dc) const
 {
     CView::Dump(dc);
 }
 
 CDTDemoDoc* CDTDemoView::GetDocument() // non-debug version is inline
 {
     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDTDemoDoc)));
     return (CDTDemoDoc*)m_pDocument;
 }
 #endif //_DEBUG
 
 /////////////////////////////////////////////////////////////////////////////
 // CDTDemoView message handlers
 
 void CDTDemoView::OnInitialUpdate() 
 {
     CView::OnInitialUpdate();
     CRect ClientRect(0, 0, 1000, 1000);
     if (m_ToolTip.Create(this, TTS_ALWAYSTIP) && m_ToolTip.AddTool(this))
     {
         m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX);
         m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_AUTOPOP, SHRT_MAX);
         m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_INITIAL, 200);
         m_ToolTip.SendMessage(TTM_SETDELAYTIME, TTDT_RESHOW, 200);
     }
     else
     {
         TRACE("Error in creating ToolTip");
     }
 }
 
 BOOL CDTDemoView::OnToolTipNeedText(UINT id, NMHDR * pNMHDR, LRESULT * pResult)
 {
     BOOL bHandledNotify = FALSE;
 
     CPoint CursorPos;
     VERIFY(::GetCursorPos(&CursorPos));
     ScreenToClient(&CursorPos);
 
     CRect ClientRect;
     GetClientRect(ClientRect);
 
     // Make certain that the cursor is in the client rect, because the
     // mainframe also wants these messages to provide tooltips for the
     // toolbar.
     if (ClientRect.PtInRect(CursorPos))
     {
         TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
         m_pCircleHit = HitTest(CursorPos);
 
         if (m_pCircleHit)
         {
             // Adjust the text by filling in TOOLTIPTEXT
             CString strTip;
             const CPoint& Center = m_pCircleHit->GetCenter();
             COLORREF Color = m_pCircleHit->GetColor();
             strTip.Format("Center:  (%d, %d)\nRadius:  %d\nColor:  (%d, %d, %d)", 
                           Center.x, Center.y, m_pCircleHit->GetRadius(), 
                           (int)GetRValue(Color), (int)GetGValue(Color),  
                           (int)GetBValue(Color));
             ASSERT(strTip.GetLength() < sizeof(pTTT->szText));
             ::strcpy(pTTT->szText, strTip);
 
             // Set the text color to same color as circle
             m_ToolTip.SendMessage(TTM_SETTIPTEXTCOLOR, Color, 0L);
         }
         else
         {
             pTTT->szText[0] = 0;
         }
         bHandledNotify = TRUE;
     }
     return bHandledNotify;
 }
 
 BOOL CDTDemoView::PreTranslateMessage(MSG* pMsg) 
 {
     if (::IsWindow(m_ToolTip.m_hWnd) && pMsg->hwnd == m_hWnd)
     {
         switch(pMsg->message)
         {
         case WM_LBUTTONDOWN:    
         case WM_MOUSEMOVE:
         case WM_LBUTTONUP:    
         case WM_RBUTTONDOWN:
         case WM_MBUTTONDOWN:    
         case WM_RBUTTONUP:
         case WM_MBUTTONUP:
             m_ToolTip.RelayEvent(pMsg);
             break;
         }
     }
     return CView::PreTranslateMessage(pMsg);
 }
 
 void CDTDemoView::OnMouseMove(UINT nFlags, CPoint point) 
 {
     if (::IsWindow(m_ToolTip.m_hWnd))
     {
         const CCircle* pCircleHit = HitTest(point);
 
         if (!pCircleHit || pCircleHit != m_pCircleHit)
         {
             // Use Activate() to hide the tooltip.
             m_ToolTip.Activate(FALSE);        
         }
 
         if (pCircleHit)
         {
             m_ToolTip.Activate(TRUE);
             m_pCircleHit = pCircleHit;
         }
     }
     CView::OnMouseMove(nFlags, point);
 }

Figure 11   CTitleTip


 /////////////////////////////////////////////////////////////////////////////
 // CTitleTip window
 
 class CTitleTip : public CWnd
 {
 public:
     CTitleTip();
 
     virtual BOOL Create(CListBox* pParentWnd);
 
     virtual void Show(CRect DisplayRect, int nItemIndex);
     virtual void Hide();
 
 // Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CTitleTip)
     //}}AFX_VIRTUAL
 
 // Implementation
 public:
     virtual ~CTitleTip();
 
 protected:
     const int m_nNoIndex;        // Not a valid index
     static LPCSTR m_pszWndClass; // Registered class name
     int m_nItemIndex;            // Index of currently displayed listbox item
     CListBox* m_pListBox;        // Parent listbox
 
     BOOL IsListBoxOwnerDraw();
 
     // Generated message map functions
 protected:
     //{{AFX_MSG(CTitleTip)
     afx_msg void OnPaint();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
 };
 
 /////////////////////////////////////////////////////////////////////////////
 // TitleTip.cpp : implementation file
 //
 
 #include "stdafx.h"
 #include "TitleTip.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 /////////////////////////////////////////////////////////////////////////////
 // CTitleTip
 
 LPCSTR CTitleTip::m_pszWndClass = NULL;
 
 CTitleTip::CTitleTip()
 :m_nNoIndex(-1)
 {
     // Register the window class if it has not already been registered by
     // previous instantiation of CTitleTip.
     if (m_pszWndClass == NULL)
     {
         m_pszWndClass = AfxRegisterWndClass(
             CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW);
     }
     m_nItemIndex = m_nNoIndex;
     m_pListBox = NULL;
 }
 
 CTitleTip::~CTitleTip()
 {
 }
 
 BOOL CTitleTip::Create(CListBox* pParentWnd)
 {
     ASSERT_VALID(pParentWnd);
     m_pListBox = pParentWnd;
 
     // Don't add border to regular (non owner-draw) listboxes because
     // owner-draw item automatically adds border.
     DWORD dwStyle = WS_POPUP;
     if (!IsListBoxOwnerDraw())
     {
         dwStyle |= WS_BORDER;
     }
 
     return CreateEx(0, m_pszWndClass, NULL, 
         dwStyle, 0, 0, 0, 0,
         pParentWnd->GetSafeHwnd(), NULL, NULL);
 }
 
 BOOL CTitleTip::IsListBoxOwnerDraw()
 {
     ASSERT_VALID(m_pListBox);
     DWORD dwStyle = m_pListBox->GetStyle();
     return (dwStyle & LBS_OWNERDRAWFIXED) || (dwStyle & LBS_OWNERDRAWVARIABLE);
 }
 
 void CTitleTip::Show(CRect DisplayRect, int nItemIndex)
 {
     ASSERT_VALID(m_pListBox);
     ASSERT(nItemIndex < m_pListBox->GetCount()); 
     ASSERT(nItemIndex >= 0);
     ASSERT(::IsWindow(m_hWnd));
     ASSERT(!DisplayRect.IsRectEmpty());
 
     // Invalidate if new item.
     if (m_nItemIndex != nItemIndex)
     {
         m_nItemIndex = nItemIndex;
         InvalidateRect(NULL);
     }
 
     // Adjust window position and visibility.
     CRect WindowRect;
     GetWindowRect(WindowRect);
     int nSWPFlags = SWP_SHOWWINDOW | SWP_NOACTIVATE;
     if (WindowRect == DisplayRect)
     {
         nSWPFlags |= SWP_NOMOVE | SWP_NOSIZE;
     }
     VERIFY(SetWindowPos(&wndTopMost, DisplayRect.left, DisplayRect.top,
                         DisplayRect.Width(), DisplayRect.Height(), nSWPFlags));
 }
 
 void CTitleTip::Hide()
 {
     ASSERT(::IsWindow(m_hWnd));
     ShowWindow(SW_HIDE);
 }
 
 
 BEGIN_MESSAGE_MAP(CTitleTip, CWnd)
     //{{AFX_MSG_MAP(CTitleTip)
     ON_WM_PAINT()
     //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 
 /////////////////////////////////////////////////////////////////////////////
 // CTitleTip message handlers
 
 void CTitleTip::OnPaint() 
 {
     ASSERT(m_nItemIndex != m_nNoIndex);
 
     CPaintDC DC(this);
 
     int nSavedDC = DC.SaveDC();
 
     CRect ClientRect;
     GetClientRect(ClientRect);
 
     if (IsListBoxOwnerDraw())
     {
         // Let the listbox do the real drawing.
         DRAWITEMSTRUCT DrawItemStruct;
 
         DrawItemStruct.CtlType = ODT_LISTBOX;
         DrawItemStruct.CtlID = m_pListBox->GetDlgCtrlID();
         DrawItemStruct.itemID = m_nItemIndex;
         DrawItemStruct.itemAction = ODA_DRAWENTIRE;
         DrawItemStruct.hwndItem = m_pListBox->GetSafeHwnd();
         DrawItemStruct.hDC = DC.GetSafeHdc();
         DrawItemStruct.rcItem = ClientRect;
         DrawItemStruct.itemData = m_pListBox->GetItemData(m_nItemIndex);
         DrawItemStruct.itemState = (m_pListBox->GetSel(m_nItemIndex) > 0 ? 
                                     ODS_SELECTED : 0);
         if (m_pListBox->GetStyle() & LBS_MULTIPLESEL)
         {
             if (m_pListBox->GetCaretIndex() == m_nItemIndex)
             {
                 DrawItemStruct.itemState |= ODS_FOCUS;
             }
         }
         else
         {
             DrawItemStruct.itemState |= ODS_FOCUS;
         }
 
         m_pListBox->DrawItem(&DrawItemStruct);
     }
     else
     {
         // Do all of the drawing ourselves
         CFont* pFont = m_pListBox->GetFont();
         ASSERT_VALID(pFont);
         DC.SelectObject(pFont);
 
         COLORREF clrBackground = RGB(255, 255, 255);
         if (m_pListBox->GetSel(m_nItemIndex) > 0)
         {
             DC.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
             clrBackground = ::GetSysColor(COLOR_HIGHLIGHT);
         }
 
         // Draw background
         DC.FillSolidRect(ClientRect, clrBackground);
 
         // Draw text of item
         CString strItem;
         m_pListBox->GetText(m_nItemIndex, strItem);
         ASSERT(!strItem.IsEmpty());
         DC.SetBkMode(TRANSPARENT);
         DC.TextOut(1, -1, strItem);
     }
 
     DC.RestoreDC(nSavedDC);
 
     // Do not call CWnd::OnPaint() for painting messages
 }

Figure 12   CTitleTipListBox


 // TitleTipListBox.h : header file
 //
 
 /////////////////////////////////////////////////////////////////////////////
 // CTitleTipListBox window
 
 #ifndef __TITLETIPLISTBOX_H__
 #define __TITLETIPLISTBOX_H__
 
 #include "TitleTip.h"
 
 class CTitleTipListBox : public CListBox
 {
 // Construction
 public:
     CTitleTipListBox();
 
 // Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CTitleTipListBox)
     public:
     virtual BOOL PreTranslateMessage(MSG* pMsg);
     //}}AFX_VIRTUAL
 
 // Implementation
 public:
     virtual ~CTitleTipListBox();
 
 protected:
     const int m_nNoIndex; // Not a valid index
     CPoint m_LastMouseMovePoint; // Last position of mouse cursor
     BOOL m_bMouseCaptured; // Is mouse captured?
     CTitleTip m_TitleTip; // TitleTip that gets displayed when necessary
 
     // This method should be overridden by an owner-draw listbox.
     virtual int GetIdealItemRect(int nIndex, LPRECT lpRect);
 
     void AdjustTitleTip(int nNewIndex);
     void CaptureMouse();
     BOOL IsAppActive();
 
     // Generated message map functions
 protected:
     //{{AFX_MSG(CTitleTipListBox)
     afx_msg void OnMouseMove(UINT nFlags, CPoint point);
     afx_msg void OnSelchange();
     afx_msg void OnKillFocus(CWnd* pNewWnd);
     afx_msg void OnDestroy();
     afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
     afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
     //}}AFX_MSG
     afx_msg LONG OnContentChanged(UINT, LONG);
 
     DECLARE_MESSAGE_MAP()
 };
 
 #endif // __TITLETIPLISTBOX_H__
 
 /////////////////////////////////////////////////////////////////////////////
 // TitleTipListBox.cpp : implementation file
 //
 
 #include "stdafx.h"
 #include "TitleTipListBox.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 /////////////////////////////////////////////////////////////////////////////
 // CTitleTipListBox
 
 CTitleTipListBox::CTitleTipListBox()
 : m_LastMouseMovePoint(0, 0) , m_nNoIndex(-1)
 {
     m_bMouseCaptured = FALSE;
 }
 
 CTitleTipListBox::~CTitleTipListBox()
 {
     ASSERT(!m_bMouseCaptured);
 }
 
 int CTitleTipListBox::GetIdealItemRect(int nIndex, LPRECT lpRect)
 {
     // Calculate the ideal rect for an item. The ideal rect is dependent
     // on the length of the string. This only works for regular 
     // (non owner-draw)listboxes.
     ASSERT(lpRect);
     ASSERT(nIndex >= 0);
     DWORD dwStyle = GetStyle();
     int    nStatus = GetItemRect(nIndex, lpRect);    
     if (nStatus != LB_ERR && !(dwStyle & LBS_OWNERDRAWFIXED) && 
         !(dwStyle & LBS_OWNERDRAWVARIABLE))
     {
         CString strItem;
         GetText(nIndex, strItem);
         if (!strItem.IsEmpty())
         {
             // Calulate the ideal text length.
             CClientDC DC(this);
             CFont* pOldFont = DC.SelectObject(GetFont());
             CSize ItemSize = DC.GetTextExtent(strItem);
             DC.SelectObject(pOldFont);
 
             // Take the maximum of regular width and ideal width.
             const int cxEdgeSpace = 2;
             lpRect->right = max(lpRect->right, 
                 lpRect->left + ItemSize.cx + (cxEdgeSpace * 2));
         }
     }
     else
     {
         TRACE("Owner-draw listbox detected - override CTitleTipListBox::GetIdeaItemRect()\n");
     }
     return nStatus;
 }
 
 void CTitleTipListBox::AdjustTitleTip(int nNewIndex)
 {
     if (!::IsWindow(m_TitleTip.m_hWnd))
     {
         VERIFY(m_TitleTip.Create(this));
     }
 
     if (nNewIndex == m_nNoIndex)
     {
         m_TitleTip.Hide();
     }
     else
     {
         CRect IdealItemRect;
         GetIdealItemRect(nNewIndex, IdealItemRect);
         CRect ItemRect;
         GetItemRect(nNewIndex, ItemRect);
         if (ItemRect == IdealItemRect)
         {
             m_TitleTip.Hide();
         }
         else
         {
             // Adjust the rect for being near the edge of screen.
             ClientToScreen(IdealItemRect);
             int nScreenWidth = ::GetSystemMetrics(SM_CXFULLSCREEN);
             if (IdealItemRect.right > nScreenWidth)
             {
                 IdealItemRect.OffsetRect(nScreenWidth - IdealItemRect.right, 0);
             }
             if (IdealItemRect.left < 0)
             {
                 IdealItemRect.OffsetRect(-IdealItemRect.left, 0);
             }
 
             m_TitleTip.Show(IdealItemRect, nNewIndex);  
         }
     }
 
     if (m_TitleTip.IsWindowVisible())
     {
         // Make sure we capture mouse so we can detect when to turn off 
         // title tip.
         if (!m_bMouseCaptured && GetCapture() != this)
         {
             CaptureMouse();
         }
     }
     else
     {
         // The tip is invisible so release the mouse.
         if (m_bMouseCaptured)
         {
             VERIFY(ReleaseCapture());
             m_bMouseCaptured = FALSE;
         }
     }
 }
 
 void CTitleTipListBox::CaptureMouse()
 {
     ASSERT(!m_bMouseCaptured);
     CPoint Point;
     VERIFY(GetCursorPos(&Point));
     ScreenToClient(&Point);
     m_LastMouseMovePoint = Point;
     SetCapture();
     m_bMouseCaptured = TRUE;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // CTitleTipListBox message handlers
 
 
 LONG CTitleTipListBox::OnContentChanged(UINT, LONG)
 {
     // Turn off title tip.
     AdjustTitleTip(m_nNoIndex);
     return Default();
 }
 
 
 void CTitleTipListBox::OnMouseMove(UINT nFlags, CPoint point) 
 {
     if (point != m_LastMouseMovePoint && IsAppActive())
     {
         m_LastMouseMovePoint = point;
 
         int nIndexHit = m_nNoIndex;
 
         CRect ClientRect;
         GetClientRect(ClientRect);
         if (ClientRect.PtInRect(point))
         {
             // Hit test.
             for (int n = 0; nIndexHit == m_nNoIndex && n < GetCount(); n++)
             {
                 CRect ItemRect;
                 GetItemRect(n, ItemRect);
                 if (ItemRect.PtInRect(point))
                 {
                     nIndexHit = n;    
                 }
             }
         }
         AdjustTitleTip(nIndexHit);
     }
     CListBox::OnMouseMove(nFlags, point);
 }
 
 
 void CTitleTipListBox::OnSelchange() 
 {
     int nSelIndex;
     if (GetStyle() & LBS_MULTIPLESEL)
     {
         nSelIndex = GetCaretIndex();    
     }
     else
     {
         nSelIndex = GetCurSel();
     }
     AdjustTitleTip(nSelIndex);
     m_TitleTip.InvalidateRect(NULL);
     m_TitleTip.UpdateWindow();
 }
 
 void CTitleTipListBox::OnKillFocus(CWnd* pNewWnd) 
 {
     CListBox::OnKillFocus(pNewWnd);
     if (pNewWnd != &m_TitleTip)
     {
         AdjustTitleTip(m_nNoIndex);
     }
 }
 
 void CTitleTipListBox::OnDestroy() 
 {
     AdjustTitleTip(m_nNoIndex);
     m_TitleTip.DestroyWindow();
     CListBox::OnDestroy();
 }
 
 void CTitleTipListBox::OnLButtonDown(UINT nFlags, CPoint point) 
 {
     // Temporarily disable mouse capturing because the base class may 
     // capture the mouse.
 
     if (m_bMouseCaptured)
     {
         ReleaseCapture();
         m_bMouseCaptured = FALSE;
     }
 
     CListBox::OnLButtonDown(nFlags, point);
 
     if (m_TitleTip.IsWindowVisible())
     {
         m_TitleTip.InvalidateRect(NULL);
         if (this != GetCapture())
         {
             CaptureMouse();
         }
     }
 }
 
 void CTitleTipListBox::OnLButtonUp(UINT nFlags, CPoint point) 
 {
     CListBox::OnLButtonUp(nFlags, point);
 
     if (this != GetCapture() && m_TitleTip.IsWindowVisible())
     {
         CaptureMouse();
     }
 }
 
 
 BOOL CTitleTipListBox::PreTranslateMessage(MSG* pMsg) 
 {
     switch (pMsg->message)
     {
         case WM_RBUTTONDOWN:
         case WM_RBUTTONUP:
         case WM_LBUTTONDBLCLK:
         case WM_RBUTTONDBLCLK:
             // Make the active view because that is the default
             // behaviour caused by WM_MOUSEACTIVATE when NO TitleTip
             // is over this window.
             AdjustTitleTip(m_nNoIndex);
             CFrameWnd* pFrameWnd = GetParentFrame();
             if (pFrameWnd)
             {
                 BOOL bDone = FALSE;
                 CWnd* pWnd = this;
                 while (!bDone)
                 {
                     pWnd = pWnd->GetParent();
                     if (!pWnd || pWnd == pFrameWnd)
                     {
                         bDone = TRUE;
                     }
                     else if (pWnd->IsKindOf(RUNTIME_CLASS(CView)))
                     {
                         pFrameWnd->SetActiveView((CView*)pWnd);
                         bDone = TRUE;
                     }
                 }
             }
             break;
     }
     
     return CListBox::PreTranslateMessage(pMsg);
 }

Figure 13   CODListBox


 // ODListBox.h : header file
 //
 
 /////////////////////////////////////////////////////////////////////////////
 // CODListBox window
 
 #include "TitleTipListBox.h"
 
 class CODListBox : public CTitleTipListBox
 {
 // Construction
 public:
     CODListBox();
 
 // Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CODListBox)
     public:
     virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
     virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
     //}}AFX_VIRTUAL
 
 // Implementation
 public:
     virtual ~CODListBox();
 
 protected:
     const int m_nEdgeSpace; // Extra space surrounding text
     const int m_nFontHeight; // Height of font
     CFont m_Font; // Font used for displaying item
 
     virtual int GetIdealItemRect(int nIndex, LPRECT lpRect);
 
     // Generated message map functions
 protected:
     //{{AFX_MSG(CODListBox)
         // NOTE - the ClassWizard will add and remove member functions here.
     //}}AFX_MSG
 
     DECLARE_MESSAGE_MAP()
 };
 
 /////////////////////////////////////////////////////////////////////////////
 // ODListBox.cpp : implementation file
 //
 
 #include "stdafx.h"
 #include "TTDemo.h"
 #include "ODListBox.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 /////////////////////////////////////////////////////////////////////////////
 // CODListBox
 
 CODListBox::CODListBox()
 : m_nEdgeSpace(4), m_nFontHeight(20) 
 {
     VERIFY(m_Font.CreateFont(m_nFontHeight, 0, 0, 0, FW_BOLD, 0, 0, 0, 
                              ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, 
                              DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
                              "Arial"));
 }
 
 CODListBox::~CODListBox()
 {
 }
 
 int CODListBox::GetIdealItemRect(int nIndex, LPRECT lpRect)
 {
     ASSERT(nIndex >= 0);
 
     int nResult = GetItemRect(nIndex, lpRect);
 
     if (nResult != LB_ERR)
     {
         CClientDC DC(this);
         CFont* pOldFont = DC.SelectObject(&m_Font);
 
         // Calculate the text length.
         CString strItem;
         GetText(nIndex, strItem);
         CSize TextSize = DC.GetTextExtent(strItem);
 
         // Take the maximum of the regular or ideal.
         lpRect->right = max(lpRect->right, 
             lpRect->left + TextSize.cx + (m_nEdgeSpace * 2));
 
         DC.SelectObject(pOldFont);
     }
     return nResult;
 }
 
 BEGIN_MESSAGE_MAP(CODListBox, CTitleTipListBox)
     //{{AFX_MSG_MAP(CODListBox)
         // NOTE - the ClassWizard will add and remove mapping macros here.
     //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
 // CODListBox message handlers
 
 void CODListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
 {
     CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
     ASSERT_VALID(pDC);
     int nSavedDC = pDC->SaveDC();
 
     CString strItem;
     if (lpDrawItemStruct->itemID != -1)
     {
         GetText(lpDrawItemStruct->itemID, strItem);
     }
 
     COLORREF TextColor;
     COLORREF BackColor;
     UINT nItemState = lpDrawItemStruct->itemState; 
     if (nItemState & ODS_SELECTED)
     {
         TextColor = RGB(255, 255, 255); // White
         BackColor = RGB(255, 0, 0); // Red
     }
     else
     {
         TextColor = RGB(255, 0, 0);  // Red
         BackColor = RGB(255, 255, 255); // White
     }
     
     CRect ItemRect(lpDrawItemStruct->rcItem);
 
     // Draw background
     pDC->FillSolidRect(ItemRect, BackColor);
 
     // Draw text
     pDC->SetTextColor(TextColor);
     pDC->SetBkMode(TRANSPARENT);
     pDC->SelectObject(&m_Font);
     ItemRect.left += m_nEdgeSpace;
     pDC->DrawText(strItem, ItemRect, 
         DT_LEFT | DT_SINGLELINE | DT_VCENTER);
     ItemRect.left -= m_nEdgeSpace;
 
     // Draw focus rect if necessary
     if (nItemState & ODS_FOCUS)
     {
         pDC->DrawFocusRect(ItemRect);
     }
 
     pDC->RestoreDC(nSavedDC);
 }
 
 void CODListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
 {
     lpMeasureItemStruct->itemHeight = m_nFontHeight + (m_nEdgeSpace * 2);    
 }

Figure 14   CTTDemoDlg


 // TTDemoDlg.h : header file
 /////////////////////////////////////////////////////////////////////////////
 // CTTDemoDlg dialog
 
 #include "TitleTipListBox.h"
 #include "ODListBox.h"
 
 class CTTDemoDlg : public CDialog
 {
 // Construction
 public:
     CTTDemoDlg(CWnd* pParent = NULL);    // standard constructor
 
 // Dialog Data
     //{{AFX_DATA(CTTDemoDlg)
     enum { IDD = IDD_TTDEMO_DIALOG };
         // NOTE: the ClassWizard will add data members here
     //}}AFX_DATA
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CTTDemoDlg)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
     //}}AFX_VIRTUAL
 
 // Implementation
 protected:
     HICON m_hIcon;
     CTitleTipListBox m_RegListBox; // Regular listbox
     CODListBox m_ODListBox; // Owner-draw listbox
 
     // Generated message map functions
     //{{AFX_MSG(CTTDemoDlg)
     virtual BOOL OnInitDialog();
     afx_msg void OnPaint();
     afx_msg HCURSOR OnQueryDragIcon();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
 };
 
 // TTDemoDlg.cpp : implementation file
 
 #include "stdafx.h"
 #include "TTDemo.h"
 #include "TTDemoDlg.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 /////////////////////////////////////////////////////////////////////////////
 // CTTDemoDlg dialog
 
 CTTDemoDlg::CTTDemoDlg(CWnd* pParent /*=NULL*/)
     : CDialog(CTTDemoDlg::IDD, pParent)
 {
     //{{AFX_DATA_INIT(CTTDemoDlg)
         // NOTE: the ClassWizard will add member initialization here
     //}}AFX_DATA_INIT
     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 }
 void CTTDemoDlg::DoDataExchange(CDataExchange* pDX)
 {
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CTTDemoDlg)
         // NOTE: the ClassWizard will add DDX and DDV calls here
     //}}AFX_DATA_MAP
 }
 
 BEGIN_MESSAGE_MAP(CTTDemoDlg, CDialog)
     //{{AFX_MSG_MAP(CTTDemoDlg)
     ON_WM_PAINT()
     ON_WM_QUERYDRAGICON()
     //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
 // CTTDemoDlg message handlers
 
 BOOL CTTDemoDlg::OnInitDialog()
 {
     CDialog::OnInitDialog();
 
     // Set the icon for this dialog.  The framework does this automatically
     //  when the application's main window is not a dialog
     SetIcon(m_hIcon, TRUE);            // Set big icon
     SetIcon(m_hIcon, FALSE);        // Set small icon
 
     // Subclass regular listbox
     HWND hwndRegListBox = ::GetDlgItem(GetSafeHwnd(), IDC_REGLISTBOX);
     ASSERT(hwndRegListBox);
     VERIFY(m_RegListBox.SubclassWindow(hwndRegListBox));
 
     // Subclass owner-draw listbox
     HWND hwndODListBox = ::GetDlgItem(GetSafeHwnd(), IDC_ODLISTBOX);
     ASSERT(hwndODListBox);
     VERIFY(m_ODListBox.SubclassWindow(hwndODListBox));
 
     // Load both listboxes with items
     static char* pszItemArray[] =
     {
         "The C++ Programming Language",
         "C++ Primer",
         "OLE Controls Inside Out",
         "Inside OLE 2nd Edition",
         "Inside ODBC",
         "Code Complete",
         "Rapid Software Development",
         "The Design Of Everyday Things",
         "Object-Oriented Analysis And Design",
         "MFC Internals",
         "Animation Techniques In Win32",
         "Inside Visual C++",
         "Writing Solid Code",
         "Learn Java Now"
     };
     static int nItemArrayCount = sizeof(pszItemArray) / sizeof(pszItemArray[0]);
     for (int n = 0; n < nItemArrayCount; n++)
     {
         VERIFY(m_RegListBox.AddString(pszItemArray[n]) != LB_ERR);
         VERIFY(m_ODListBox.AddString(pszItemArray[n]) != LB_ERR);
     }
 
     return TRUE;  // return TRUE  unless you set the focus to a control
 }
 
 // If you add a minimize button to your dialog, you will need the code below
 //  to draw the icon.  For MFC applications using the document/view model,
 //  this is automatically done for you by the framework.
 
 void CTTDemoDlg::OnPaint() 
 {
     if (IsIconic())
     {
         CPaintDC dc(this); // device context for painting
         SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
         // Center icon in client rectangle
         int cxIcon = GetSystemMetrics(SM_CXICON);
         int cyIcon = GetSystemMetrics(SM_CYICON);
         CRect rect;
         GetClientRect(&rect);
         int x = (rect.Width() - cxIcon + 1) / 2;
         int y = (rect.Height() - cyIcon + 1) / 2;
 
         // Draw the icon
         dc.DrawIcon(x, y, m_hIcon);
     }
     else
     {
         CDialog::OnPaint();
     }
 }
 
 // The system calls this to obtain the cursor to display while the user drags
 //  the minimized window.
 HCURSOR CTTDemoDlg::OnQueryDragIcon()
 {
     return (HCURSOR) m_hIcon;
 }