实现“气球式”工具提示

时间:2021-06-17 00:17:25

首先:把下面这个头文件加入到你要实现动态显示的工程中:

#if !defined(AFX_TOOLTIPWND_H__2C52D3E4_2F5B_11D2_8FC9_000000000000__INCLUDED_)
#define AFX_TOOLTIPWND_H__2C52D3E4_2F5B_11D2_8FC9_000000000000__INCLUDED_

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

// ToolTipWnd.h : header file

// Acknowledgements:
// Thanks to Venkatesh who helped me in calculating the intersecting
// point in the ellipse.

/////////////////////////////////////////////////////////////////////////////
// CToolTipWnd window
struct BTOOLINFO {

     HWND      hwndTool;
     CString strToolText;
     COLORREF clrToolTextClr;

};

class CToolTipWnd : public CWnd
{

private:
// Construction
     LPCTSTR lpWndCls;
public:
     CToolTipWnd();
     HWND pCurrwnd;
     // Attributes
public:
     void RelayEvent(LPMSG);
     BOOL Create(CWnd*);
     bool m_bStuck;

     void AddTool(CWnd *pWnd, CString strText, COLORREF clrTextColor=NULL);
     void SetWidth(int iWidth) { m_iWidth = iWidth; }
     void SetHeight(int iHeight) { m_iHeight = iHeight; }
     void SetBkColor(COLORREF clrRef) { m_clrBkColor = clrRef; }
     void SetFrameColor(COLORREF clrRef) { m_clrFrameColor = clrRef; }
     void SetDefTextColor(COLORREF clrRef) { m_clrTextColor = clrRef; }
     void SetFontHeight(int iHeight) { m_iFontHeight = iHeight; }
     void SetFontName(CString strFontName) { m_strFontName = strFontName; }

private:
     CRgn rgn;
     CRgn rgnComb;
     CRgn rgnTri;
     CRect m_RectText;
     CFont m_fontText;
    
     CString m_strText;
     bool m_bMouseIn;
     COLORREF m_clrTextColor;
     COLORREF m_clrBkColor;
     COLORREF m_clrFrameColor;
     CMapPtrToPtr m_ToolPtr;
     int m_iWidth;
     int m_iHeight;
     int m_iFontHeight;
     CString m_strFontName;

     HWND m_hParentWnd;
public:

// Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CToolTipWnd)
     protected:
     //}}AFX_VIRTUAL
    

// Implementation
public:
     virtual ~CToolTipWnd();

     // Generated message map functions
protected:
     //{{AFX_MSG(CToolTipWnd)
     afx_msg void OnPaint();
     afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TOOLTIPWND_H__2C52D3E4_2F5B_11D2_8FC9_000000000000__INCLUDED_)

复制到这 之后,停一下。。。。。

///////

第二步,然后把下面这个ToolTipWnd,cpp也加入到该工程中

// ToolTipWnd.cpp : implementation file
//

#include "stdafx.h"
#include "ToolTipWnd.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CToolTipWnd

CToolTipWnd::CToolTipWnd()
{

     lpWndCls      = AfxRegisterWndClass(0);
    
     //Defaults
     m_bMouseIn      = false;
     m_bStuck      = false;
    
     m_iWidth = 100;
     m_iHeight = 60;

     m_clrBkColor = RGB(249,254,188); //light yellow
     m_clrFrameColor = RGB(0,0,255); //blue
     m_clrTextColor = RGB(0,0,0);       //black

     m_iFontHeight = 14;
     m_strFontName = "Arial";

     pCurrwnd = NULL;
}

CToolTipWnd::~CToolTipWnd()
{

     BTOOLINFO *stToolInfo;
     CWnd *pWnd;
     for(POSITION pos = m_ToolPtr.GetStartPosition(); pos != NULL;)
     {
           m_ToolPtr.GetNextAssoc(pos, (void *&)pWnd, (void*&) stToolInfo);
           delete stToolInfo;
     }
     m_ToolPtr.RemoveAll();

}


BEGIN_MESSAGE_MAP(CToolTipWnd, CWnd)
     //{{AFX_MSG_MAP(CToolTipWnd)
     ON_WM_PAINT()
     ON_WM_CREATE()
     //}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CToolTipWnd message handlers
BOOL CToolTipWnd::Create(CWnd* pParentWnd)
{

     BOOL bRet = CWnd::CreateEx(NULL, lpWndCls, NULL,
           WS_POPUP, 0, 0, m_iWidth, m_iHeight,
           pParentWnd->GetSafeHwnd(), NULL, NULL);

     m_hParentWnd = pParentWnd->GetSafeHwnd();
    
     if(bRet)
           SetOwner(pParentWnd);

     return bRet;

}

void CToolTipWnd::OnPaint()
{
     CPaintDC dc(this); // device context for painting
    
     CRect rectCl;
     GetClientRect(&rectCl);

     CRgn rgnComb;     
     rgnComb.CreateRectRgn(rectCl.left+10,rectCl.top,rectCl.right,rectCl.bottom);

     int iRetComb = rgnComb.CombineRgn(&rgnTri, &rgn, RGN_OR);
     if(iRetComb==ERROR)
     {
           AfxMessageBox("ERROR in Combining Region");
           return;
     }

     CBrush pBrush;
     pBrush.CreateSolidBrush(m_clrFrameColor);
    
     CBrush pBrush1;
     pBrush1.CreateSolidBrush(m_clrBkColor);
    
     dc.FillRgn( &rgnComb, &pBrush1);
     dc.FrameRgn(&rgnComb, &pBrush, 2, 1);

     dc.SetBkMode(TRANSPARENT);
     dc.SetTextColor(m_clrTextColor);
    
     CFont *pFont = dc.SelectObject(&m_fontText);
     //dc.Rectangle(&m_RectText);

     CSize czTextWidth = dc.GetTextExtent(m_strText);
    
     if( czTextWidth.cx < m_RectText.Width())
           dc.DrawText(m_strText, m_RectText, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
     else
           dc.DrawText(m_strText, m_RectText, DT_CENTER | DT_WORDBREAK);
    
    

     dc.SelectObject(pFont);
    
}


int CToolTipWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
     if (CWnd::OnCreate(lpCreateStruct) == -1)
           return -1;
    
     CRect rectCl;
     GetClientRect(&rectCl);

     int x=0, y=0;
     CRect rectTemp;

     rectTemp = rectCl;
     rectTemp.left = rectTemp.left + 10;
    
     x = (int)( (float)((float)rectTemp.Width() / 2.0) / 1.41421);
     y = (int)( (float)((float)rectTemp.Height() / 2.0) / 1.41421);

     m_RectText.top = ( (rectTemp.Height() / 2) - y);
     m_RectText.left = ( (rectTemp.Width() / 2) - x) + 10;
     m_RectText.right = ( (rectTemp.Width() / 2) + x) + 10;
     m_RectText.bottom = ( (rectTemp.Height() / 2) + y);

     rgn.m_hObject = NULL;
     rgnTri.m_hObject = NULL;
     rgnComb.m_hObject = NULL;

     BOOL bRegRet = rgn.CreateEllipticRgn(rectCl.left+10,rectCl.top,rectCl.right,rectCl.bottom);
          
     CPoint ptTri[3];
     ptTri[0].x = rectCl.left;
     ptTri[0].y = (rectCl.bottom / 2) - 10;

     ptTri[1].x = rectCl.left + 15;
     ptTri[1].y = (rectCl.bottom / 2) - 5;

     ptTri[2].x = rectCl.left + 15;
     ptTri[2].y = (rectCl.bottom / 2) + 5;
    
     ptTri[3].x = rectCl.left;
     ptTri[3].y = (rectCl.bottom / 2) - 10;

     BOOL bRegTriRet = rgnTri.CreatePolygonRgn(ptTri, 3, ALTERNATE);

     rgnComb.CreateRectRgn(rectCl.left+10,rectCl.top,rectCl.right,rectCl.bottom);
     int iRetComb = rgnComb.CombineRgn(&rgnTri, &rgn, RGN_OR);

     if(iRetComb == ERROR)
     {
           AfxMessageBox("ERROR in Combining Region");
           return -1;
     }

     int bRgnWnd = SetWindowRgn(rgnComb.operator HRGN( ), TRUE);     

     m_fontText.CreateFont(m_iFontHeight, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,m_strFontName);     
    
     return 0;
}

void CToolTipWnd::RelayEvent(LPMSG lpMsg)
{

     switch(lpMsg->message)
     {
     case WM_KEYDOWN:
                 if(IsWindowVisible())
                 {           
                       ShowWindow(SW_HIDE);
                 }
                 break;

     case WM_LBUTTONDOWN:
     case WM_RBUTTONDOWN:
                 if(IsWindowVisible())
                 {
                       ShowWindow(SW_HIDE);
                 }
                 break;

     case WM_MOUSEMOVE:
           {
                 CWnd *pFocusWnd = AfxGetApp()->m_pMainWnd->GetFocus();
                 if(pFocusWnd==NULL)
                       break;
                 CWnd* pWnd = CWnd::FromHandle(lpMsg->hwnd);

                 HWND hWndTemp = ::GetParent(lpMsg->hwnd);
                
                 CPoint pt;
                 pt.x = lpMsg->pt.x;
                 pt.y = lpMsg->pt.y;
                
                 BTOOLINFO *stToolInfo;
                 CWnd *pBToolWnd;
                
                 for(POSITION pos = m_ToolPtr.GetStartPosition(); pos != NULL;)
                 {
                       m_ToolPtr.GetNextAssoc(pos, (void *&)pBToolWnd, (void*&) stToolInfo);
                      
                       if(!m_bMouseIn)
                       {
                             if(lpMsg->hwnd == stToolInfo->hwndTool)
                             {
                            
                                   if(m_bStuck && IsWindowVisible())
                                   {
                                         SetWindowPos(&wndTop,pt.x,pt.y,m_iWidth,m_iHeight,SWP_NOACTIVATE);
                                         ShowWindow(SW_SHOWNOACTIVATE);
                                   }

                                   m_bMouseIn = true;
                                   m_clrTextColor = stToolInfo->clrToolTextClr;
                                   m_strText = stToolInfo->strToolText;
                                  
                                   SetWindowPos(&wndTop,pt.x,pt.y,m_iWidth,m_iHeight,SWP_NOACTIVATE);
                                   ShowWindow(SW_SHOWNOACTIVATE);
                                  
                                   pCurrwnd = stToolInfo->hwndTool;

                                   break;
                             }
                       }
                       else
                       {
                             CRect rect;
                             ::GetWindowRect(pCurrwnd, &rect);
                             if(m_bStuck && IsWindowVisible())
                             {
                                   SetWindowPos(&wndTop,pt.x,pt.y,m_iWidth,m_iHeight,SWP_NOACTIVATE);
                                   ShowWindow(SW_SHOWNOACTIVATE);
                             }
                            
                            
                             CWnd* pWnd = CWnd::FromHandle(lpMsg->hwnd);
                             CWnd *WndPt = pWnd->WindowFromPoint(lpMsg->pt);
                             if(WndPt->GetSafeHwnd() != pCurrwnd)
                             {
                                   m_bMouseIn = false;
                                   ShowWindow(SW_HIDE);
                             }
                            

                             break;
                       }
                 }
                
           }     
           break; //WM_MOUSEMOVE
     }
     
}

void CToolTipWnd::AddTool(CWnd *pWnd, CString strText, COLORREF clrTextColor)
{

     BTOOLINFO *stToolInfo;

     if(!m_ToolPtr.Lookup( pWnd, ( void*& ) stToolInfo))
     {
           stToolInfo = new BTOOLINFO;
           stToolInfo->hwndTool = pWnd->GetSafeHwnd();
           stToolInfo->strToolText = strText;
           if(clrTextColor==NULL)
                 stToolInfo->clrToolTextClr = m_clrTextColor;
           else
                 stToolInfo->clrToolTextClr = clrTextColor;
    
           m_ToolPtr.SetAt(pWnd, stToolInfo);
     }

}


第三步,把第一步的头文件包含在要用到气球式提示的cpp中,

第四步,

在对话框类中添加成员变量: m_BalloonToolTip,类型为:CToolTipWnd

然后在对话框中的 OnInitDialog()函数中 添加如下代码:

m_BalloonToolTip.Create(this);
m_BalloonToolTip..AddTool(GetDlgItem(控件ID号),"提示信息",RGB(60,50,100));

接下来要重新实现虚函数PreTranslateMessage(Msg* pMsg)

在对话框类的头文件中加入这一个函数定义:

BOOL PreTranslateMessage(Msg* pMsg);

然后

在重新实现该函数,也就是在cpp文件中加入下面这段代码

BooL CTestDlg::PreTranslateMessage(Msg* pMsg)

{

if(m_BalloonToolTip)
   m_BalloonToolTip.RelayEvent(pMsg);

}

最后,编译,如果你人品好的话,恭喜你了,大功告成