在图形绘制领域,经常需要用到透明的信息提示窗口,比如当鼠标移动到一个图元上,显示该图元对象的实时数据(如设备名称、状态、实测数据等),当鼠标移开,及时隐藏该提示框;比如在曲线控件绘图时,随着鼠标移动,实时展示曲线对应的横纵坐标值等
各种通用开发库里,也有类似的控件,如C# WinForm的ToolTip,1>风格比较厚实,透明度不好,遮挡住了底部的图形;2>Tip窗口显示和隐藏不灵敏,感觉顿挫。浮云绘图将在本文详细设计和实现透明信息提示框CFyToolTip类。
一、透明提示框的需求描述
1. 需支持显示和隐藏功能;
2. 需支持设定指定显示位置功能;
3. 需支持设置或动态计算提示框的窗口大小尺寸;
4. 需支持提示框背景色和边框颜色;
5. 需支持提示框文本字体类型和字号设置;
6. 需支持多行信息提示;
7. 需支持每行设置不同的文本内容和字体颜色。
二、透明提示框的定义(C++)
根据上文的开发需求和上图的展示效果,相应属性定义如下代码。
显示Tip框方法有4个:
void ShowTips(CPoint& point, CRect& parentRect, CString* vals, int* colors, int count)用于实现上图实时测量多行文本多颜色字体的信息提示,当然也可以用于所有情形信息提示。
void ShowXTips(int xCenter, int yTop, CString strTipsContent)用于实现上图X轴上的居中显示的信息提示框。
void ShowY1Tips(int xRight, int yCenter, CString strTipsContent)用于显示左侧Y轴的信息提示。
void ShowY2Tips(int xLeft, int yCenter, CString strTipsContent)用于显示右侧Y轴的信息提示。
1 // 作者:浮云绘图,专业付费定制图形编辑器、工控曲线、报表等各类绘图软件 2 // QQ:316868127 3 4 class CFyToolTip : public CWnd 5 { 6 private: 7 CPoint m_ptOrg; //左上角坐标 8 CSize m_TipSize; 9 CString m_strTips; 10 COLORREF m_clrBack = 0xE0E0E0; 11 COLORREF m_clrText = 0x000000; 12 COLORREF m_clrFrameColor; 13 14 bool m_bMultLineTip = false; //多行不同颜色文字Tip模式标记 15 CString* m_values = NULL; //多行文本 16 int* m_colors = NULL; //多行字体颜色 17 int m_lineCount = 0; 18 19 CString m_fontName = "微软雅黑"; 20 int m_fontSize = 8; 21 22 // Construction 23 public: 24 CFyToolTip(); 25 virtual ~CFyToolTip(); 26 27 public: 28 BOOL Create(CWnd* pWnd, BYTE bAlpha, DWORD dwStyle= WS_POPUP); 29 30 void SetToolTipSize(CSize szSize); 31 32 void ShowTips(int nX, int nY, CString strTipsContent); 33 34 void HideTips(); 35 36 void SetBkColor(COLORREF clrBack); 37 38 void SetTipTextColor(COLORREF clrText); 39 40 void SetFrameColor(COLORREF clrFrame); 41 42 void ShowTips(CPoint& point, CRect& parentRect, CString* vals, int* colors, int count); 43 44 void ShowXTips(int xCenter, int yTop, CString strTipsContent); 45 void ShowY1Tips(int xRight, int yCenter, CString strTipsContent); 46 void ShowY2Tips(int xLeft, int yCenter, CString strTipsContent); 47 48 int GetTipsTextHeight(); 49 50 void DrawTransparentRect(CDC* dc, CRect* rect); 51 52 protected: 53 //{{AFX_MSG(CFyToolTip) 54 afx_msg void OnPaint(); 55 afx_msg BOOL OnEraseBkgnd(CDC* pDC); 56 //}}AFX_MSG 57 DECLARE_MESSAGE_MAP() 58 };
三、透明提示框的实现
请看OnPaint函数,先重绘客户区背景和边框 --> 再画各行文本(文字、字体和颜色)。
每当调用ShowTips函数时,根据传入的显示起点和显示内容,重新计算Tip窗口的大小位置,再调用OnPaint完成重绘。
1 BOOL CFyToolTip::Create(CWnd* pWnd, BYTE bAlpha, DWORD dwStyle) 2 { 3 LPCTSTR lpszClassName = AfxRegisterWndClass(NULL); 4 BOOL bRet = CreateEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, lpszClassName, _T(""), //WS_EX_TOOLWINDOW WS_EX_TOPMOST 5 dwStyle, 0, 0, 1, 1, pWnd->GetSafeHwnd(), NULL); // WS_POPUP WS_CHILD 6 if (bRet) 7 { 8 CSize sz(30,20); 9 MoveWindow(CRect(m_ptOrg, sz)); 10 m_TipSize = sz; 11 } 12 13 LONG para = GetWindowLong(this->GetSafeHwnd(), GWL_EXSTYLE); 14 para |= WS_EX_LAYERED; 15 SetWindowLong(this->GetSafeHwnd(), GWL_EXSTYLE, para); 16 SetLayeredWindowAttributes(RGB(0, 0, 0), bAlpha, LWA_ALPHA); //LWA_ALPHA 17 18 m_parent = pWnd; 19 return bRet; 20 } 21 22 void CFyToolTip::OnPaint() 23 { 24 CPaintDC dc(this); // device context for painting 25 26 // TODO: Add your message handler code here 27 CRect rtWin; 28 GetClientRect(rtWin); 29 dc.FillSolidRect(rtWin, m_clrBack); 30 dc.SetBkMode(TRANSPARENT); 31 dc.Draw3dRect(rtWin, m_clrFrameColor, m_clrFrameColor); 32 33 CFont font; 34 font.CreatePointFont(m_fontSize * 10, m_fontName, NULL); 35 CFont* pOldFont = dc.SelectObject(&font); 36 37 CRect curLineRect; 38 if (!m_bMultLineTip) 39 { 40 CSize txtSize = dc.GetTextExtent(m_strTips); // 字串的长度像素 41 curLineRect.SetRect(rtWin.left + 0, 1, rtWin.right, txtSize.cy); 42 dc.SetTextColor(m_clrText); 43 dc.DrawText(m_strTips, rtWin, DT_LEFT | DT_VCENTER | DT_SINGLELINE); 44 } 45 else 46 { 47 int offsetY = 1; 48 for (int i = 0; i < m_lineCount; i++) 49 { 50 CSize txtSize = dc.GetTextExtent(m_values[i]); // 字串的长度像素 51 52 dc.SetTextColor(m_colors[i]); 53 curLineRect.SetRect(rtWin.left + 4, offsetY, rtWin.right, offsetY + txtSize.cy); 54 dc.DrawText(m_values[i], curLineRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); 55 56 offsetY += txtSize.cy + 1; 57 } 58 } 59 dc.SelectObject(pOldFont); 60 } 61 62 63 64 void CFyToolTip::ShowTips(int nX, int nY, CString strTipsContent) 65 { 66 m_bMultLineTip = false; 67 68 69 m_strTips = strTipsContent; 70 71 //计算字符串长宽 72 HDC hDC = ::GetDC(this->m_hWnd); 73 CDC dc; 74 dc.Attach(hDC); 75 76 CFont font; 77 font.CreatePointFont(m_fontSize * 10, m_fontName, NULL); 78 CFont* pOldFont = dc.SelectObject(&font); 79 CSize txtSize = dc.GetTextExtent(m_strTips); // 字串的长度像素 80 dc.SelectObject(pOldFont); 81 82 //设置窗口位置大小 83 m_ptOrg = CPoint(nX, nY); 84 m_TipSize= txtSize; 85 MoveWindow(nX, nY, m_TipSize.cx, m_TipSize.cy, TRUE); 86 if (!IsWindowVisible()) 87 { 88 ShowWindow(SW_SHOW); 89 } 90 Invalidate(TRUE); 91 } 92 93 void CFyToolTip::ShowXTips(int xCenter, int yTop, CString strTipsContent) 94 { 95 m_bMultLineTip = false; 96 97 m_strTips = strTipsContent; 98 99 //计算字符串长宽 100 HDC hDC = ::GetDC(this->m_hWnd); 101 CDC dc; 102 dc.Attach(hDC); 103 104 CFont font; 105 font.CreatePointFont(m_fontSize * 10, m_fontName, NULL); 106 CFont* pOldFont = dc.SelectObject(&font); 107 CSize txtSize = dc.GetTextExtent(m_strTips); // 字串的长度像素 108 dc.SelectObject(pOldFont); 109 110 //设置窗口位置大小 111 int nX = xCenter - txtSize.cx / 2; 112 m_ptOrg = CPoint(nX, yTop); 113 m_TipSize = txtSize; 114 MoveWindow(nX, yTop, m_TipSize.cx + 2, m_TipSize.cy, TRUE); 115 if (!IsWindowVisible()) 116 { 117 ShowWindow(SW_SHOW); 118 } 119 Invalidate(TRUE); 120 } 121 122 void CFyToolTip::ShowY1Tips(int xRight, int yCenter, CString strTipsContent) 123 { 124 ...... 125 } 126 127 void CFyToolTip::ShowY2Tips(int xLeft, int yCenter, CString strTipsContent) 128 { 129 ...... 130 } 131 132 void CFyToolTip::ShowTips(CPoint& point, CRect& parentRect, CString* vals, int* colors, int count) 133 { 134 ...... 135 } 136 137 void CFyToolTip::HideTips() 138 { 139 if (IsWindow(m_hWnd)) 140 { 141 ShowWindow(SW_HIDE); 142 } 143 } 144 145 146 void CFyToolTip::SetBkColor(COLORREF clrBack) 147 { 148 m_clrBack = clrBack; 149 } 150 151 void CFyToolTip::SetFrameColor(COLORREF clrFrame) 152 { 153 m_clrFrameColor = clrFrame; 154 } 155 156 void CFyToolTip::SetTipTextColor(COLORREF clrText) 157 { 158 m_clrText = clrText; 159 } 160 .....
本文完整了实现了上图展示的3中透明信息提示框,可以满足绝大多数对透明窗口信息提示框的功能要求。如果在某些特性场景下,还需要支持FyTipTool提示框支持鼠标 点击、移动等事件,可以继续扩展。需增加以下事件支持:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void onm ouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void onm ouseLeave();
四、CFyToolTip使用示例
1 //1声明变量,Y轴上左Tip 2 CFyToolTip* m_y1ToolTip; 3 4 5 //2构造变量,透明度200/255 6 m_y1ToolTip = new CFyToolTip(); 7 m_y1ToolTip->Create(this, 200); 8 9 10 //3显示、隐藏提示框 11 if (GetPointInArea(point)) 12 { 13 CString syTip = GetYToolTip(point); 14 int y1Right; 15 GetYAxesTipXPos(virpoint, y1Right, y2Left); 16 if (y1Right > 0) 17 { 18 CPoint tmpPt(y1Right, point.y); 19 ClientToScreen(&tmpPt); 20 m_y1ToolTip->ShowY1Tips(tmpPt.x, tmpPt.y, syTip); 21 } 22 } 23 else 24 { 25 m_y1ToolTip->HideTips(); 26 }