9. ÅøÆÁ°ú ŸÀÌÆ²ÆÁ(Tooltip & Titletip)    

9.1 Çì´õ¸¦ À§ÇÑ ÅøÆÁ                      

Çì´õÄÁÆ®·ÑÀ» À§ÇÑ ÅøÆÁÀ» Ãß°¡Çϴ°ÍÀº ¸Å¿ì °£´ÜÇÕ´Ï´Ù.

CListCtrl ÆÄ»ý Ŭ·¡½º¿¡ CToolTipCtrl ŸÀÔÀÇ ¸â¹öº¯¼ö¸¦ ¼±¾ðÇÕ´Ï´Ù.

CListCtrl ÆÄ»ý Ŭ·¡½º¿¡¼­ PreSubclassWindow() ÇÔ¼ö¸¦ ¿À¹ö¶óÀ̵å ÇÕ´Ï´Ù. º£À̽ºÅ¬·¡½ºÀÇ PreSubclassWindow()¸¦ È£ÃâÇÑÈÄ, ÅøÆÁ°´Ã¼¸¦ ¸¸µì´Ï´Ù. OnCreate()´ë½Å¿¡ PreSubclassWindow() ¸¦ ¿À¹ö¶óÀ̵åÇÑ ÀÌÀ¯´Â ÄÁÆ®·ÑÀº º¸Åë ´ÙÀ̾ó·Î±× ¸®¼Ò½º·ÎºÎÅÍ »ý¼ºµÊÀ¸·Î½á ÀÌ¹Ì ¸¸µé¾îÁøÈÄ¿¡ C++ °´Ã¼¿¡ ºÙ±â(Attach) ¶§¹®¿¡, OnCreate°¡ °´Ã¼¿¡ ´ëÇØ ÀüÇô È£ÃâÀÌ µÇÁö ¾Ê±â¶§¹®ÀÔ´Ï´Ù.

void CMyListCtrl::PreSubclassWindow()
{
        CListCtrl::PreSubclassWindow();

        // Add initialization code
        m_tooltip.Create( this );
        m_tooltip.AddTool( GetDlgItem(0), "Right click for context menu" );
}

PreTranslateMessage()ÇÔ¼ö¸¦ ¿À¹ö¶óÀ̵åÇØ¼­ CToolTip °´Ã¼ÀÇ RelayEvents()ÇÔ¼ö¸¦ È£ÃâÇÕ´Ï´Ù.

BOOL CMyListCtrl::PreTranslateMessage(MSG* pMsg)
{
        m_tooltip.RelayEvent( pMsg );
        return CListCtrl::PreTranslateMessage(pMsg);
}

9.2 °¢°¢ÀÇ Ä÷³ Çì´õ¸¦ À§ÇÑ ÅøÆÁ

Ä÷³Çì´õ¿¡ ÅøÆÁÀ» Á¦°øÇϴ°ÍÀº ¿©·¯ ¿ëµµ°¡ ÀÖ½À´Ï´Ù. Çì´õÅøÆÁÀÌ Á¤¸» À¯¿ëÇÏ´Ù°í ´À³¤ ÇѰ¡Áö °æ¿ì´Â Ä÷³ÀÇ ³ÐÀ̰¡ Á¦ÇѵǾî ÀÖÀ»¶§ ÀÔ´Ï´Ù. ÅøÆÁÀº Ä÷³Çì´õ°¡ Á¦ÇÑµÈ ³ÐÀ̶§¹®¿¡ Àü´ÞÇÏÁö ¸øÇÏ´Â °ÍÀ» Àü´ÞÇÒ ¼ö ÀÖ½À´Ï´Ù. Äڵ带 ModularÇÏ°Ô Çϱâ À§ÇØ ¿ì¸®´Â CListCtrl ÆÄ»ýŬ·¡½º¿¡ ÅøÆÁ±â´ÉÀ» ±¸ÇöÇÒ°ÍÀÔ´Ï´Ù.

CListCtrl ÆÄ»ý Ŭ·¡½º¿¡ CToolTipCtrl ŸÀÔÀÇ ¸â¹öº¯¼ö¸¦ ¼±¾ðÇÕ´Ï´Ù.

CListCtrl ÆÄ»ý Ŭ·¡½º¿¡¼­ PreSubclassWindow() ÇÔ¼ö¸¦ ¿À¹ö¶óÀ̵å ÇÕ´Ï´Ù. º£À̽ºÅ¬·¡½ºÀÇ PreSubclassWindow()¸¦ È£ÃâÇÑÈÄ, ÅøÆÁ°´Ã¼¸¦ ¸¸µì´Ï´Ù.

void CMyListCtrl::PreSubclassWindow()
{
        CListCtrl::PreSubclassWindow();
        // Add initialization code
        m_tooltip.Create( this );
}

PreTranslateMessage()ÇÔ¼ö¸¦ ¿À¹ö¶óÀ̵åÇØ¼­ CToolTip °´Ã¼ÀÇ RelayEvents()ÇÔ¼ö¸¦ È£ÃâÇÕ´Ï´Ù. RelayEvents() ÇÔ¼ö¸¦ È£ÃâÇϴ°ÍÀº ÅøÆÁÀÌ ¸¶¿ì½º°¡ Åø¿µ¿ª ¾îµð¿¡ µé¾î¿Ô´ÂÁö´Â ¾Ë ¼ö ÀÖ´Â ±âȸ¸¦ Á¦°øÇÕ´Ï´Ù. ºñ·Ï ¸®½ºÆ®ºäÄÁÆ®·ÑÀÌ ¹Þ´Â ¸ðµç ¸Þ½ÃÁö¸¦ ÆÐ½ºÇÏÁö¸¸, ÅøÆÁÄÁÆ®·ÑÀº WM_?BUTTONDOWN,WM_?BUTTONUP, ±×¸®°í WM_MOUSEMOVE ¸Þ½ÃÁö ¸¸À» ó¸®ÇÕ´Ï´Ù.

BOOL CMyListCtrl::PreTranslateMessage(MSG* pMsg)
{
        m_tooltip.RelayEvent( pMsg );
        return CListCtrl::PreTranslateMessage(pMsg);
}

ÅøÆÁÀ» Ãß°¡ÇϱâÀ§ÇÑ ¹æ¹ýÀ» Á¦°øÇÕ´Ï´Ù. ÇϳªÀÇ ÅøÆÁÄÁÆ®·ÑÀº ¿©·¯°³ÀÇ ÅøÀ» ó¸®ÇÒ¼ö ÀÖ½À´Ï´Ù. AddHeaderToolTip() ÇïÆÛ ÇÔ¼ö´Â ÅøÆÁÄÁÆ®·Ñ¿¡ »õ·Î¿î ÅøÀ» Ãß°¡ÇÕ´Ï´Ù

// AddHeaderToolTip     -       Ä÷³Çì´õ¿¡ ´ëÇÑ ÅøÆÁÀ» Ãß°¡ÇÕ´Ï´Ù.
//                                         ÄÁÆ®·ÑÀº ÀÚ¼¼È÷º¸±â(LVS_REPORT) ¸ðµå¿©¾ß ÇÕ´Ï´Ù.
// Returns                    -       ¼º°ø½Ã TRUE ¸®ÅÏ
// nCol                         -       Ä÷³ À妽º
// sTip                         -       ÅøÆÁÅØ½ºÆ®

BOOL CMyListCtrl::AddHeaderToolTip(int nCol, LPCTSTR sTip /*= NULL*/)
{
        const int TOOLTIP_LENGTH = 80;
        char buf[TOOLTIP_LENGTH+1];
        
        CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
        int nColumnCount = pHeader->GetItemCount();
        if( nCol >= nColumnCount)       return FALSE;
        if( (GetStyle() & LVS_TYPEMASK) != LVS_REPORT)  return FALSE;
        
        // Çì´õÀÇ ³ôÀ̸¦ ±¸ÇÕ´Ï´Ù.
        RECT rect;
        pHeader->GetClientRect( &rect );
        int height = rect.bottom;

        RECT rctooltip;
        rctooltip.top = 0;
        rctooltip.bottom = rect.bottom;

        // Ä÷³ÀÇ Á¿ì Å׵θ®¸¦ ±¸ÇÕ´Ï´Ù.
        rctooltip.left = 0 - GetScrollPos( SB_HORZ );
        for( int i = 0; i < nCol; i++ ) rctooltip.left += GetColumnWidth( i );
        rctooltip.right = rctooltip.left + GetColumnWidth( nCol );

        if( sTip == NULL )      {
                // Ä÷³ Çìµù ¹®ÀÚ¿­À» °¡Á®¿É´Ï´Ù.
                LV_COLUMN       lvcolumn;
                lvcolumn.mask           = LVCF_TEXT;
                lvcolumn.pszText                = buf;
                lvcolumn.cchTextMax     = TOOLTIP_LENGTH;
                if( !GetColumn( nCol, &lvcolumn ) )     return FALSE;
    }
        m_tooltip.AddTool( GetDlgItem(0), sTip ? sTip : buf, &rctooltip, nCol+1 );    return TRUE;
}

OnNotify()¸¦ ¿À¹ö¶óÀ̵åÇÏ¿© Ä÷³ ³ÐÀÌ¿¡ ´ëÇÑ º¯µ¿»çÇ×À» ÃßÀûÇÕ´Ï´Ù.¸¸¾à »ç¿ëÀÚ°¡ Ä÷³Å©±â¸¦ Á¶Á¤ÇßÀ»¶§ ÅøÆÁÁ¤º¸¸¦ °»½ÅÇÏÁö ¾Ê´Â´Ù¸é ÅøÆÁÀº Á¤È®ÇÑ Ä÷³À» º¸¿©ÁÖÁö ¾ÊÀ»°ÍÀÔ´Ï´Ù.

BOOL CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
        HD_NOTIFY   *pHDN = (HD_NOTIFY*) lParam;

        if((pHDN->hdr.code == HDN_ENDTRACKA || pHDN->hdr.code == HDN_ENDTRACKW))   {
                // ÅøÆÁÀÇ Á¤º¸¸¦ °»½ÅÇÕ´Ï´Ù.
                CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
                int nColumnCount = pHeader->GetItemCount();
                CToolInfo toolinfo;
                toolinfo.cbSize = sizeof( toolinfo );

                // ¿µÇâÀ» ¹ÞÀº °¢ Ä÷³À» tooltipinfo ¸¦ ÅëÇØ ¼øÈ¯(Cycle)ÇÕ´Ï´Ï´Ù.
                for( int i = pHDN->iItem; i <= nColumnCount; i++ )        {
                        m_tooltip.GetToolInfo( toolinfo, pHeader, i + 1 );
                        int dx;                 // ³ÐÀÌÀÇ º¯°æ»çÇ×À» ÀúÀåÇÕ´Ï´Ù.
                        if( i == pHDN->iItem )  
                               dx = pHDN->pitem->cxy - toolinfo.rect.right;
                        else                                            
                               toolinfo.rect.left += dx;
                        toolinfo.rect.right += dx;
                        m_tooltip.SetToolInfo( &toolinfo );
                }
        }
        return CListCtrl::OnNotify(wParam, lParam, pResult);
}

 

9.3 °¢ ¼¿À» À§ÇÑ ÅøÆÁ

ÅøÆÁÀº Ä÷³³ÐÀ̰¡ Á¦ÇÑµÈ È­¸é »çÀÌÁî¶§¹®¿¡ Á¦Çѵɶ§ ¸Å¿ì À¯¿ëÇÕ´Ï´Ù. À̵éÀº ´ÜÃàµÇ¼­ º¸¿©Áö´Â Ä÷³ÀÇ ¹®ÀÚ¿­µéÀ» È®ÀåÇϴµ¥ »ç¿ëµÉ ¼öµµ ÀÖ½À´Ï´Ù. ÀÌ ÀÏÀ»À§ÇØ MFC ¿¡ ÀÇÇØ Á¦°øµÇ´Â ÅøÆÁ±â´ÉÀ» »ç¿ëÇÒ°ÍÀÔ´Ï´Ù. ¾Æ·¡ÄÚµå´Â ¼¿ÀÇ ¹®ÀÚ¿­À» ÅøÆÁ¿¡ º¸¿©ÁÝ´Ï´Ù¸¸, À̰ÍÀº ½±°Ô ÀÌ¹Ì ¼¿¿¡ º¸¿©ÁÖ°í Àִ°ÍÀ» º¸À̱⺸´Ù Á» ´Ù¸¥°ÍÀ» º¸¿©ÁÖ´Â °ÍÀ¸·Î ¼öÁ¤µÉ ¼ö ÀÖ½À´Ï´Ù.

°¢ ¼¿¿¡ ´ëÇØ ÅøÆÁÀ» Ãß°¡ÇÏ´Â °ÍÀº »ó´çÈ÷ ½±½À´Ï´Ù. ¾î·µç ¹®¼­´Â º°·Î µµ¿òÀ̵ÇÁö ¸øÇϸç, ÀνÄÇØ¾ßÇÒ ¸î°¡Áö »çÇ×ÀÌ ÀÖ½À´Ï´Ù. NT 4.0 °ú Win95 ÀÇ ¸®½ºÆ®ºä ÄÁÆ®·ÑÀº ¸î°¡Áö Áß¿äÇÑ Â÷ÀÌÁ¡À» °¡Áö°í ÀÖ½À´Ï´Ù. ù°·Î , ¸®½ºÆ® ºä ÄÁÆ®·Ñ°úÅøÆÁ ÄÁÆ®·ÑÀº Windows 95 »ó¿¡¼­´Â ANSI ÄÁÆ®·ÑÀÔ´Ï´Ù. À̰ÍÀÌ ÀǹÌÇÏ´Â °ÍÀº Windows 95 »ó¿¡¼­´Â ¸Þ½ÃÁöµéÀÌ ANSI ¹öÁ¯À̶ó´Â °ÍÀÔ´Ï´Ù. ÇÁ·ÎÁ§Æ® ¼¼ÆÃ¿¡ µû¶ó¼­ ¸Þ½ÃÁö »ó¼öµé(A ³ª W Á¢¹Ì¾î°¡ ¾ø´Â°Í)ÀÌ ¸Â´Â°ªÀ¸·Î º¯È¯µÇ´Â°Í¿¡ ÀÇÁ¸ÇÏÁö¸¶½Ê½Ã¿À. ¿¹¸¦ µé¾î UNICODE ÇÁ·Î±×·¥À» °³¹ßÇÑ´Ù¸é TTN_NEEDTEXT´Â TTN_NEEDTEXTW·Î ¹ø¿ªµÇ¾î¾ß ÇÕ´Ï´Ù. ÇÏÁö¸¸ Windows 95 ¿¡¼­´Â ½ÇÁ¦ ¸Þ½ÃÁö´Â TTN_NEEDTEXTA ÀÔ´Ï´Ù. À̰ÍÀº ¶ÇÇÑ ±¸Á¶Ã¼¿Í ¹®ÀÚ¿­¿¡µµ Àû¿ëµË´Ï´Ù. Windows 95 ¿¡¼­´Â ÄÁÆ®·Ñ¿¡Àü´ÞµÇ´Â ¸ðµç ¹®ÀÚ¿­Àº ANSI ¹®ÀÚ¿­À̾î¾ß ÇÕ´Ï´Ù. NT 4.0¿¡¼± ÄÁÆ®·ÑµéÀÌ ¸ðµÎUNICODE ÄÁÆ®·ÑÀÔ´Ï´Ù.

µÑ°·Î, NT 4.0 ¿¡¼­´Â ¸®½ºÆ®ºä ÄÁÆ®·ÑÀÌ ÀÚµ¿À¸·Î ÅøÆÁÄÁÆ®·ÑÀ» ¸¸µì´Ï´Ù. ÀÌ ³»Àå ÅøÆÁ ÄÁÆ®·ÑÀº ¸¶¿ì½º°¡ ¸®½ºÆ®ºä ÄÁÆ®·ÑÀ§¿¡ ¿Ã¶ó¿Í Àá½Ã ¿òÁ÷ÀÌÁö ¾ÊÀ» ¶§ ÀÚµ¿ÀûÀ¸·Î TTN_NEEDTEXTW ÅëÁö¸¦ º¸³»°Ô µË´Ï´Ù. ¾Æ·¡ÄÚµå´Â ÀÌ ³»Àå ÅøÆÁ ÄÁÆ®·Ñ·ÎºÎÅÍÀÇ ÅëÁö¸¦ ¹«½ÃÇÕ´Ï´Ù.

PreSubclassWindow()¸¦ ¿À¹ö¶óÀ̵åÇÏ°í º£À̽º Ŭ·¡½ºÀÇ PreSubclassWindow()¸¦ È£ÃâÇÑÈÄ EnableToolTips(TRUE)¸¦ È£ÃâÇÕ´Ï´Ù. EnableToolTips()ÇÔ¼ö´Â CWnd Ŭ·¡½ºÀǸâ¹ö ÇÔ¼öÀ̰í, µû¶ó¼­ ¸ðµç À©µµ¿ì¿Í ÄÁÆ®·Ñ¿¡ »ç¿ëÀÌ °¡´ÉÇÕ´Ï´Ù.

void CMyListCtrl::PreSubclassWindow()
{
        CListCtrl::PreSubclassWindow();
        // Add initialization code
        EnableToolTips(TRUE);
}

OnToolHitTest()ÇÔ¼ö¸¦ ¿À¹ö¶óÀ̵å ÇÕ´Ï´Ù. OnToolHitTest()´Â CWndŬ·¡½º¿¡ Á¤ÀÇµÈ °¡»óÇÔ¼öÀ̰í ÇÁ·¹ÀÓ¿öÅ©¿¡ ÀÇÇØ¼­ ¸¶¿ì½ºÆ÷ÀÎÅͰ¡ ¾î¶²ÅøÀ§¿¡ ÀÖ´ÂÁö °áÁ¤ÇϱâÀ§ÇØÈ£ÃâµË´Ï´Ù. ÅøÀº ÄÁÆ®·Ñ À©µµ¿ìÀ̰ųª ¶Ç´Â À©µµ¿ì¾ÈÀÇ »ç°¢Çü ¿µ¿ªÀϼöµµ ÀÖ½À´Ï´Ù¿ì¸®ÀÇ ¸ñÀûÀ» À§Çؼ± °¢ ¼¿ÀÇ ¿µ¿ªÀÌ Åø·Î 󸮵Ǿî¾ß ÇÕ´Ï´Ù.

OnToolHitTest() ¹®¼­(Documentation)´Â ÅøÀ» ã¾Ò´Ù¸é 1À» ¸øÃ£¾Ò´Ù¸é -1À» ¸®ÅÏÇ϶ó°í ¾Ï½ÃÇÕ´Ï´Ù.ÇÏÁö¸¸ ½ÇÁ¦·Î ÇÁ·¹ÀÓ¿öÅ©´Â ¸®ÅϰªÀ» ÅøÀÌ ¹Ù²î¾ú´ÂÁö °áÁ¤Çϴµ¥ »ç¿ëÇÕ´Ï´Ù. ÇÁ·¹ÀÓ¿öÅ©´Â ÅøÀÌ ¹Ù²î¾úÀ»¶§¸¸ ÅøÆÁÀ» °»½ÅÇϹǷÎ, OnToolHitTest()´Â ÁöÁ¤µÈ Á¡ÀÇ ¼¿ÀÌ °ª(¹®ÀÚ¿­)ÀÌ ¹Ù²î¾î¾úÀ»¶§ ´Ù¸¥ °ªÀ» ¸®ÅÏÇØ¾ß ÇÕ´Ï´Ù.

int CMyListCtrl::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
{
        int             row, col;
        RECT    cellrect;
        row = CellRectFromPoint(point, &cellrect, &col );
        if ( row == -1 )        return -1;

        pTI->hwnd               = m_hWnd;
        pTI->uId                = (UINT)((row<<10)+(col&0x3ff)+1);
        pTI->lpszText   = LPSTR_TEXTCALLBACK;
        pTI->rect               = cellrect;
        return  pTI->uId;
}

ÀÌ ÇÔ¼ö´Â óÀ½¿¡ CellRectFromPoint() ÇÔ¼ö¸¦ È£ÃâÇÏ¿© ·Î¿ì¿Í Ä÷³, ±×¸®°í ¼¿ÀÇ °æ°è »ç°¢ÇüÀ» ¾Ë¾Æ³À´Ï´Ù.
CellRectFromPoint() ´Â ¾Æ·¡¿¡¼­ »ìÆìº¾´Ï´Ù. ÇÔ¼ö´Â±×¸®°í TOOLINFO ±¸Á¶Ã¼¸¦ ¼³Á¤ÇÕ´Ï´Ù. 'uId' ´Â ÁÙ,¿­ÀÇ °ªÀ» °áÇÕÇÑ °ªÀ» ÁöÁ¤ÇÕ´Ï´Ù. ·Î¿ì¿Í Ä÷³ÀÇ °áÇÕ¹æ¹ýÀº 4194303 °³ÀÇ ·Î¿ì¿Í 1023°³ÀÇ Ä÷³À» Çã¿ëÇÕ´Ï´Ù. ¶ÇÇÑ °á°ú¿¡ 1ÀÌ ´õÇØÁø°ÍÀ» ÁÖÀÇÇϽʽÿÀ. ÀÌ·¸°Ô ÇÏ´Â ÀÌÀ¯´Â 0ÀÌ ¾Æ´Ñ°ª¸¸À» »ý¼ºÇϱâÀ§Çؼ­ ÀÔ´Ï´Ù. ¿ì¸®´Â NT4.0 ¿¡¼­ ÀÚµ¿ÀûÀ¸·Î ¸¸µé¾îÁø ÅøÆÁ¿¡¼­ º¸³½ ÅëÁö¿Í ±¸º°Çϱâ À§Çؼ­ 0ÀÌ ¾Æ´Ñ°ªÀ» ÇÊ¿ä·Î ÇÕ´Ï´Ù. ¾Õ¿¡¼­ ¾ð±ÞÇßµíÀÌ NT 4.0 ¿¡¼­ ¸¸µé¾îÁø ¸®½ºÆ®ºä ÄÁÆ®·ÑÀº ÀÚµ¿ÀûÀ¸·Î ÅøÆÁÀ» »ý¼ºÇϸç ÀÌ ÅøÆÁÀÇ ID ´Â 0ÀÔ´Ï´Ù.

´ÙÀ½ ¿ì¸®´Â OnToolHitTest() ¿¡¼­ »ç¿ëµÈ CellRectFromPoint() ÇÔ¼ö¸¦ Á¤ÀÇÇÕ´Ï´Ù. ÀÌ ÇÔ¼ö´Â #1 - 4.4 ¿¡¼­ ´Ù·ç¾îÁø HitTestEx() ÇÔ¼ö¿Í ¸Å¿ì ºñ½ÁÇÕ´Ï´Ù. Á¡ À§Ä¡ÀÇ ·Î¿ì ¿Í Ä÷³°ªÀ» ¾Ë¾Æ³»´Â°Í¿¡ ´õÇØ¼­ ÀÌ ÇÔ¼ö´Â Á¡¾Æ·¡ ¼¿ÀÇ °æ°è »ç°¢Çüµµ ¾Ë¾Æ³À´Ï´Ù.

// CellRectFromPoint    - ¼¿ÀÇ ·Î¿ì,Ä÷³,°æ°è»ç°¢ÇüÀ» ¾Ë¾Æ³À´Ï´Ù.
// Returns                   - ¼º°ø½Ã ·Î¿ì À妽º, ¾Æ´Ï¸é -1
// point                       - °Ë»çµÉ Á¡(ÇöÀç ¸¶¿ì½º Æ÷ÀÎÅÍ)
// cellrect                    - °æ°è»ç°¢ÇüÀ» ÀúÀåÇÒ º¯¼ö
// col                          - Ä÷³°ªÀ» ÀúÀåÇÒ º¯¼ö

int CMyListCtrl::CellRectFromPoint(CPoint & point, RECT * cellrect, int * col)    const
{
        int colnum;

        // ¸®½ºÆ®ºä°¡ LVS_REPORT ¸ðµå¿¡ÀÖ´ÂÁö È®ÀÎ
        if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
             return -1;

        // ÇöÀç È­¸é¿¡ º¸ÀÌ´ÂóÀ½°ú ³¡ Row ¸¦ ¾Ë¾Æ³»±â
        int row = GetTopIndex();
        int bottom = row + GetCountPerPage();
        if( bottom > GetItemCount() )   bottom = GetItemCount();
    
        // Ä÷³°¹¼ö ¾Ë¾Æ³»±â
        CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
        int nColumnCount = pHeader->GetItemCount();

        // ÇöÀ纸ÀÌ´Â Row µé°£¿¡ ·çÇÁ µ¹±â
        for( ;row <=bottom;row++)               {
                // ¾ÆÀÌÅÛÀÇ °æ°è »ç°¢ÇüÀ» °¡Á®¿À°í, Á¡ÀÌ Æ÷ÇԵǴÂÁö üũ        
                CRect rect;
                GetItemRect( row, &rect, LVIR_BOUNDS );
                if( rect.PtInRect(point) )              {
                        // Ä÷³Ã£±â
                        for( colnum = 0; colnum < nColumnCount; colnum++ )  {
                                int colwidth = GetColumnWidth(colnum);                
                                if( point.x >= rect.left && point.x <= (rect.left + colwidth) ) {
                                        RECT rectClient;
                                        GetClientRect( &rectClient );
                                        if( col ) *col = colnum;
                                        rect.right = rect.left + colwidth;
        
                                        // ¿À¸¥ÂÊ ³¡ÀÌ Å¬¶óÀÌ¾ðÆ® ¿µ¿ªÀ» ¹þ¾î³ªÁö ¾Êµµ·Ï È®ÀÎ
                                        if( rect.right > rectClient.right )             
                                                 rect.right = rectClient.right;
                                        *cellrect = rect;

                                        return row;
                                }
                                rect.left += colwidth;
                        }
                }
        }
        return -1;
}

OnToolTipText()ÇÔ¼ö¸¦ Á¤ÀÇÇÕ´Ï´Ù. À̰ÍÀº ÅøÆÁÀ¸·ÎºÎÅÍÀÇ TTN_NEEDTEXTÅëÁö¿¡ ´ëÇÑ Çڵ鷯ÀÔ´Ï´Ù. ½ÇÁ¦·Î OnToolTipText()´Â TTN_NEEDTEXTA¿Í TTN_NEEDTEXTW ÅëÁö ¾çÂÊÀ» ó¸®Çϸç ÇÁ·Î±×·¥ÀÚ½ÅÀÌ ANSIµç UNICODEµç °ü°è¾øÀÌ ÀüÀÚ¸¦ À§ÇØ ANSI ½ºÆ®¸µÀ»»ç¿ëÇϸç ÈÄÀÚ¸¦ À§ÇØ UNICODE ½ºÆ®¸µÀ» »ç¿ëÇÕ´Ï´Ù.

BOOL CMyListCtrl::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
        // ANSI ¿Í UNICODE ¾çÂʹöÁ¯ÀÇ ¸Þ½ÃÁö¸¦ ó¸®ÇؾßÇÔ
        TOOLTIPTEXTA*   pTTTA   = (TOOLTIPTEXTA*)pNMHDR;
        TOOLTIPTEXTW*   pTTTW   = (TOOLTIPTEXTW*)pNMHDR;
        CString         strTipText;
        UINT    nID = pNMHDR->idFrom;

        if( nID == 0 )                  // NT ¿¡¼­ÀÇ ÀÚµ¿»ý¼º ÅøÆÁÀ¸·ÎºÎÅÍÀÇ ÅëÁö
                return FALSE;       // ±×³É ºüÁ®³ª°£´Ù.

        int row     = ((nID-1) >> 10) & 0x3fffff ;
        int col     = (nID-1) & 0x3ff;

        strTipText = GetItemText( row, col );

#ifndef _UNICODE
        if (pNMHDR->code == TTN_NEEDTEXTA)      
               lstrcpyn(pTTTA->szText, strTipText, 80);
        else                                                                            
               _mbstowcsz(pTTTW->szText, strTipText, 80);
#else
        if (pNMHDR->code == TTN_NEEDTEXTA)      
               _wcstombsz(pTTTA->szText, strTipText, 80);
        else                                                                            
               lstrcpyn(pTTTW->szText, strTipText, 80);
#endif
        *pResult = 0;
        return TRUE;            // ¸Þ½ÃÁö°¡ 󸮵Ǿú´Ù.
}

ÇÔ¼ö´Â ¸ÕÀú NT¿¡¼­ÀÇ ³»ÀåÅøÆÁ ÅëÁöÀÎÁö üũÇÏ°í ¸Â´Ù¸é ¹Ù·Î ¸®ÅÏÇÕ´Ï´Ù. ±×¸®°í id·ÎºÎÅÍ ·Î¿ì¿Í Ä÷³Á¤º¸¸¦ ÇØµ¶ÇÏ°í ¼¿¾ÈÀÇ Á¤º¸·Î TOOLTIPTEXT±¸Á¶Ã¼¸¦ ä¿ó´Ï´Ù.

¸Þ½ÃÁö¸Ê¿¡ OnToolTipText()¸¦ ¿¬°áÇϽʽÿÀ. ON_NOTIFY_EX ¿Í ON_NOTIFY_EX_RANGE¸ÅÅ©·Î¸¦ »ç¿ëÇÏ´Â °ÍÀÌ ÁÁ½À´Ï´Ù. À̰ÍÀº ÇÊ¿äÇÏ´Ù¸é ´õÀÌ»óÀÇ ¸Þ½ÃÁö 󸮸¦ À§ÇØ ÅëÁö¸¦ Àü´ÞÇÏ´Â °ÍÀ» °¡´ÉÇÏ°Ô ÇÕ´Ï´Ù.

BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
        //{{AFX_MSG_MAP(CMyListCtrl)
        :
        // other entries
        :
        //}}AFX_MSG_MAP
        ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
        ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()

¿ì¸®°¡ ´Ü¼øÇÑ ON_NOTIFY ¸ÅÅ©·Î¸¦ »ç¿ëÇÏÁö ¾Ê´Â´Ù´Â°Í¿¡ ÁÖÀÇÇϽʽÿÀ. ½ÇÁ¦·Î ´ç½ÅÀÌ ON_NOTIFY(TTN_NEEDTEXT, 0, OnToolTipText) °°Àº ¸Þ½ÃÁö¸Ê ¿£Æ®¸®¸¦ »ç¿ëÇÑ´Ù¸é ¸î°³ÀÇ Å« ¹®Á¦Á¡À» °¡Áö°Ô µË´Ï´Ù. ù°·Î TTN_NEEDTEXT ÅëÁö´Â ANSI ºôµå¹öÁ¯¿¡¼± NT 4.0 »ó¿¡¼­´Â Àý´ë·Î ¹ÞÀ»¼ö ¾ø´Â TTN_NEEDTEXTA ·Î º¯È¯µË´Ï´Ù. µÑ°·Î, ¿ì¸®´Â ÀϹÝÀûÀÎ °æ¿ì°¡ ¾Æ´Ñ ID 0 ÀÌ¿ÜÀÇ °ª¿¡ °ü½ÉÀÌ ÀÖ´Ù´Â °ÍÀÔ´Ï´Ù.

 

9.4 °¢ ¼¿À» À§ÇÑ Å¸ÀÌÆ²ÆÁ

ŸÀÌÆ²Àº ´Ù¼Ò ÅøÆÁ°ú ºñ½ÁÇÕ´Ï´Ù. ¸®½ºÆ®ºä ÄÁÆ®·Ñ¿¡ ´ëÇØ¼­, ŸÀÌÆ²ÆÁÀº ÅØ½ºÆ®¸¦ ´Ù º¸¿©ÁÙ¼ö ÀÖÀ»¸¸Å­ ³ÐÁö ¸øÇÒ ¶§ »ç¿ëµË´Ï´Ù. ŸÀÌÆ²ÆÁÀº ¸¶¿ì½º°¡ ¼¿ÀÇ À§¿¡ À§Ä¡ÇÏÁö¸¶ÀÚ º¸¿©Áö°Ô µË´Ï´Ù.

¿ì¸®´Â ŸÀÌÆ²ÆÁÀ» À§ÇØ Ä¿½ºÅÒŬ·¡½º¸¦ Á¤ÀÇÇÕ´Ï´Ù. ŸÀÌÆ²ÆÁÀº WM_MOUSEMOVE Çڵ鷯¿¡¼­ »ý¼ºµË´Ï´Ù. ŸÀÌÆ² ÆÁÀº ¸¶¿ì½º°¡ ¾ÆÀÌÅÛ ¹ÛÀ¸·Î ³ª°¡°Å³ª ÀÀ¿ëÇÁ·Î±×·¥ÀÌ Æ÷Ä¿½º¸¦ ÀÒÀ»¶§ ÀÚ±â ÀÚ½ÅÀ» ÆÄ±«ÇÏ°Ô µË´Ï´Ù.

OnMouseMove() ÄÚµå´Â CellRectFromPoint() ÇÔ¼ö¸¦ ·Î¿ì,Ä÷³À妽º¿Í ÇϺξÆÀÌÅÛÀÇ °æ°è¿µ¿ª »ç°¢ÇüÀ» ¾Ë¾Æ³»±â À§ÇØ »ç¿ëÇÕ´Ï´Ù. ±×¸®°í ŸÀÌÆ²ÆÁ °´Ã¼¿¡ »ç°¢Çü°ú ¾ÆÀÌÅÛ ÅØ½ºÆ®ÀÇ Á¤º¸¸¦ ³Ñ±â°Ô µË´Ï´Ù.  Å¸ÀÌÆ²ÆÁ°´Ã¼°¡ È­¸é¿¡ º¸¿©ÁúÁö¸¦ °áÁ¤ÇϰԵ˴ϴÙ.

void CMyListCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
        if( nFlags == 0 )               {       // ŸÀÌÆ²ÆÁ °´Ã¼ Enable Çϱâ À§Çؼ­
                int row, col;
                RECT cellrect;
                row = CellRectFromPoint(point, &cellrect, &col );
                if( row != -1 )         {
                        int offset = 5; // Ä÷³ ¿ÞÂÊ °æ°è¿¡¼­ºÎÅÍÀÇ ¿ÀÇÁ¼ÂÀº º¸Åë 5Çȼ¿
                        // ù¹øÂ° Ä÷³¿¡ ´ëÇØ¼± ¿ÀÇÁ¼ÂÀº ±×¸²À» °í·ÁÇØ¾ß ÇÕ´Ï´Ù.
                        // ¾Æ·¡¿ÀÇÁ¼ÂÀº ÀÚ±âÀÚ½ÅÀÇ ÇÁ·Î±×·¥¿¡ ¸Â´Â°ªÀ» »ç¿ëÇϽʽÿÀ            
                        if( col == 0 ) offset+=19;
                        m_titletip.Show( cellrect, GetItemText( row, col ), offset );        
                }
        }
        CListCtrl::OnMouseMove(nFlags, point);
}

PreSubclassWindow() ÇÔ¼ö°¡ ¿À¹ö¶óÀÌµå µÇ¾îÀÖÁö ¾Ê´Ù¸é, ÇØ¾ßÇÕ´Ï´Ù. ÀÌ ÇÔ¼ö¿¡¼­Å¸ÀÌÆ²ÆÁ °´Ã¼¸¦ »ý¼ºÇÒ°ÍÀÔ´Ï´Ù.

void CMyListCtrl::PreSubclassWindow()
{
        CListCtrl::PreSubclassWindow();
        // Add initialization code
        m_titletip.Create( this );
}

Çì´õÆÄÀϰú ±¸ÇöÆÄÀÏÀÌ ¾Æ·¡ ÀÖ½À´Ï´Ù.

#if !defined(   AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_  )
#define                 AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_

#if _MSC_VER >= 1000
        #pragma once
#endif // _MSC_VER >= 1000

// TitleTip.h : header file
// CTitleTip    window

#define TITLETIP_CLASSNAME      _T("ZTitleTip")

class CTitleTip : public CWnd
{
        public: // Construction
        CTitleTip();
        public: // Attributes
        public: // Operations
        
        //Overrides
    // ClassWizard generated virtual function overrides
        //{{AFX_VIRTUAL(CTitleTip)
    public:
        virtual BOOL PreTranslateMessage(MSG* pMsg);
    //}}AFX_VIRTUAL

        public:         // Implementation
                void Show( CRect rectTitle, LPCTSTR lpszTitleText, int xoffset = 0);
                virtual BOOL Create( CWnd *pParentWnd);
                virtual ~CTitleTip();
        protected:
                CWnd *m_pParentWnd;
                CRect m_rectTitle;

                
        protected:              // Generated message map functions
    //{{AFX_MSG(CTitleTip)
        afx_msg void OnMouseMove(UINT nFlags, CPoint point);    
        //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately// before the previous line.
#endif // !defined(AFX_TITLETIP_H__FB05F243_E98F_11D0_82A3_20933B000000__INCLUDED_)

CTitleTip ÀÇ »ý¼ºÀÚ¿¡¼­ ÀÌ ÇÁ·Î±×·¥ÀÇ ´Ù¸¥ ÀνºÅϽº¿¡¼­ À©µµ¿ìÀÇ Å¬·¡½º¸¦ µî·ÏÇÏÁö ¾Ê¾Ò´Ù¸é µî·ÏÇÏ°Ô µË´Ï´Ù. Ŭ·¡½º¿¡ ´ëÇÑ ¹è°æ ºê·¯½¬´Â COLOR_INFOBK¸¦»ç¿ëÇÏ°Ô µÇ¾î ÅøÆÁÀÇ »ö»ó°ú °°°Ô µË´Ï´Ù.

Create() ÇÔ¼ö¿¡¼­´Â ÁÖ¸ñÇØ¼­ º¼°ÍÀÌ À©µµ¿ì ½ºÅ¸ÀÔ´Ï´Ù. WS_BORDER ½ºÅ¸ÀÏÀÌ Å¸ÀÌÆ²ÆÁ À©µµ¿ì ÁÖÀ§¿¡ °æ°è¼±À» ±×¸®°Ô ÇÕ´Ï´Ù. WS_POPUP½ºÅ¸ÀÏÀº ŸÀÌÆ²ÆÁÀÌ ¸®½ºÆ®ºä ÄÁÆ®·ÑÀÇ °æ°è¸¦ ¹þ¾î³¯¼öµµ ÀÖ°Ô ÇϱâÀ§ÇØ ÇÊ¿äÇÕ´Ï´Ù. WS_EX_TOOLWINDOW½ºÅ¸ÀÏÀº À©µµ¿ì°¡ ŽºÅ©¹Ù¿¡ ³ªÅ¸³ªÁö ¾Êµµ·Ï ÇØÁÝ´Ï´Ù. WS_EX_TOPMOST ½ºÅ¸ÀÏÀº ŸÀÌÆ²ÆÁÀÌ º¸À̵µ·Ï ÇØÁÝ´Ï´Ù.

Show() ÇÔ¼ö´Â ÅØ½ºÆ®ÀÇ Å©±â°¡ ¼¿ÀÇ Å©±âº¸´Ù Ŭ¶§ ŸÀÌÆ²ÆÁÀ» º¸¿©ÁÖ°Ô µË´Ï´Ù. °æ°è »ç°¢ÇüÀº º¯ÇüµÇ°í ³ªÁß¿¡ ¾ðÁ¦ ŸÀÌÆ²ÆÁÀÌ ¼û°ÜÁ®¾ß ÇÒ¶§¸¦ °áÁ¤Çϱâ À§ÇØ ÀúÀåµË´Ï´Ù.

WM_MOUSEMOVE ¿¡ ´ëÇÑ Çڵ鷯 OnMouseMove() ´Â ŸÀÌÆ²ÆÁÀÌ º¸ÀÏ ¼¿¿µ¿ª¿¡ ¸¶¿ì½º°¡ÀÖ´ÂÁö¸¦ °Ë»çÇÕ´Ï´Ù. ÀÌ ¿µ¿ªÀº ŸÀÌÆ²ÆÁ À©µµ¿ìÀÇ Å¬¶óÀÌ¾ðÆ® ¿µ¿ª »ç°¢Çüº¸´Ù ÀÛ½À´Ï´Ù. ¸¸¾à ¸¶¿ì½º°¡ ¿µ¿ª¹ÛÀ¸·Î ³ª°£´Ù¸é ŸÀÌÆ²ÀÌ ¼û°ÜÁö°í ÀûÀýÇÑ À©µµ¿ì¿¡ WM_MOUSEMOVE ¸Þ½ÃÁö°¡ Àü´ÞµË´Ï´Ù.

ŸÀÌÆ²ÆÁÀº ¶ÇÇÑ »ç¿ëÀÚ°¡ Ű³ª ¸¶¿ì½º¹öưÀ» ´­·¶À»¶§ ¾ø¾îÁú Çʿ䰡 ÀÖ½À´Ï´Ù. ¿ì¸®´Â À̸޽ÃÁöµé¿¡ ´ëÇØ »ìÆìº¸±â À§ÇØ PreTranslateMessage()¸¦ ¿À¹ö¶óÀ̵å ÇÕ´Ï´Ù. ¸¸¾à ÀÌ ¸Þ½ÃÁöµéÁß¿¡ ¾î¶²°ÍÀÌ¶óµµ ¹Þ´Â´Ù¸é ŸÀÌÆ²ÆÁÀº ¾ø¾îÁö°í ¸®½ºÆ®ºä ÄÁÆ®·Ñ¿¡ ¸Þ½ÃÁö°¡ Àü´ÞµË´Ï´Ù.

// 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

CTitleTip::~CTitleTip()         {       }

CTitleTip::CTitleTip()
{
        // ¸¸¾à ÀÌ¹Ì µî·ÏµÇÁö ¾Ê¾Ò´Ù¸é À©µµ¿ì Ŭ·¡½º¸¦ µî·ÏÇÑ´Ù.
        WNDCLASS wndcls;
        HINSTANCE hInst = AfxGetInstanceHandle();
        if(!(::GetClassInfo(hInst, TITLETIP_CLASSNAME, &wndcls)))    {
                // »õ·Î¿î Ŭ·¡½º µî·Ï
                wndcls.style                    = CS_SAVEBITS ;
                wndcls.lpfnWndProc      = ::DefWindowProc;
                wndcls.cbClsExtra               = wndcls.cbWndExtra = 0;
                wndcls.hInstance                = hInst;
                wndcls.hIcon                    = NULL;
                wndcls.hCursor          = LoadCursor( hInst, IDC_ARROW );
                wndcls.hbrBackground    = (HBRUSH)(COLOR_INFOBK + 1);
                wndcls.lpszMenuName     = NULL;
        wndcls.lpszClassName    = TITLETIP_CLASSNAME;

                if (!AfxRegisterClass(&wndcls)) AfxThrowResourceException();
        }
}

BEGIN_MESSAGE_MAP(CTitleTip, CWnd)
        //{{AFX_MSG_MAP(CTitleTip)
        ON_WM_MOUSEMOVE()
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()

// CTitleTip message handlers

BOOL CTitleTip::Create(CWnd * pParentWnd)
{
        ASSERT_VALID(pParentWnd);

        DWORD dwStyle = WS_BORDER | WS_POPUP;  
        DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
        m_pParentWnd = pParentWnd;
    return CreateEx( dwExStyle, TITLETIP_CLASSNAME, NULL, dwStyle, 0, 0, 0, 0, NULL, NULL, NULL );
}

void CTitleTip::Show(CRect rectTitle,LPCTSTR lpszTitleText,int xoffset /*=0*/)
{
        ASSERT( ::IsWindow( m_hWnd ) );
        ASSERT( !rectTitle.IsRectEmpty() );

    if( IsWindowVisible() )             return;
        
        m_rectTitle.top = -1;
        m_rectTitle.left        = -xoffset;
        m_rectTitle.right       = rectTitle.Width()-xoffset;
        m_rectTitle.bottom      = rectTitle.Height();
        m_pParentWnd->ClientToScreen( rectTitle );

        CClientDC dc(this);
        CString strTitle(lpszTitleText);
        CFont *pFont = m_pParentWnd->GetFont();
        CFont *pFontDC = dc.SelectObject( pFont );
        CRect rectDisplay = rectTitle;
        CSizesize = dc.GetTextExtent( strTitle );

        rectDisplay.left += xoffset;
        rectDisplay.right = rectDisplay.left + size.cx + 5;
        // ¸¸¾à ÅØ½ºÆ®°¡ °ø°£¿¡ ¸Â´Ù¸é º¸ÀÌÁö ¾Ê´Â´Ù.

        if( rectDisplay.right <= rectTitle.right-xoffset )              return;

        SetWindowPos( &wndTop, rectDisplay.left, rectDisplay.top,
               rectDisplay.Width(), rectDisplay.Height(), 
               SWP_SHOWWINDOW|SWP_NOACTIVATE );

        dc.SetBkMode( TRANSPARENT );
        dc.TextOut( 0, -1, strTitle );        
        dc.SelectObject( pFontDC );
        SetCapture();
}

void CTitleTip::OnMouseMove(UINT nFlags, CPoint point)
{
        if( !m_rectTitle.PtInRect( point ) )            {
                ReleaseCapture();
                ShowWindow( SW_HIDE );

                // ¸Þ½ÃÁö¸¦ Àü´ÞÇÑ´Ù.
                ClientToScreen( &point );
                CWnd *pWnd = WindowFromPoint( point );
                if( pWnd == this ) pWnd = m_pParentWnd;
                pWnd->ScreenToClient( &point );
                pWnd->PostMessage(WM_MOUSEMOVE, nFlags, MAKELONG( point.x, point.y ));
        }
}

BOOL CTitleTip::PreTranslateMessage(MSG* pMsg)
{
        CWnd *pWnd;
        switch( pMsg->message )         {
                case WM_LBUTTONDOWN:
                case WM_RBUTTONDOWN:
                case WM_MBUTTONDOWN:
                        POINTS pts = MAKEPOINTS( pMsg->lParam );
                        POINT  point;
                        point.x = pts.x;
                        point.y = pts.y;
                        ClientToScreen( &point );
                        pWnd = WindowFromPoint( point );
                        if( pWnd == this ) pWnd = m_pParentWnd;
                        pWnd->ScreenToClient( &point );
                        pMsg->lParam = MAKELONG( point.x, point.y );        
                        // ±×³É ¹ØÀ¸·Î °¡°Ô ÇÕ´Ï´Ù.
                case WM_KEYDOWN:
                case WM_SYSKEYDOWN:
                        ReleaseCapture( );
                        ShowWindow( SW_HIDE );
                        m_pParentWnd->PostMessage(pMsg->message, pMsg->wParam, pMsg->lParam);
                        return TRUE;
        }
        if( GetFocus() == NULL )        {
                ReleaseCapture();
                ShowWindow( SW_HIDE );
                return TRUE;
        }
        return CWnd::PreTranslateMessage(pMsg);
}

 

- the end of this article -