在图形绘制领域,经常需要用到透明的信息提示窗口,比如当鼠标移动到一个图元上,显示该图元对象的实时数据(如设备名称、状态、实测数据等),当鼠标移开,及时隐藏该提示框;比如在曲线控件绘图时,随着鼠标移动,实时展示曲线对应的横纵坐标值等。
各种通用开发库里,也有类似的控件,如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轴的信息提示。
// 作者:浮云绘图,专业付费定制图形编辑器、工控曲线、报表等各类绘图软件
// QQ:316868127
class CFyToolTip : public CWnd
{
private:
CPoint m_ptOrg; //左上角坐标
CSize m_TipSize;
CString m_strTips;
COLORREF m_clrBack = 0xE0E0E0;
COLORREF m_clrText = 0x000000;
COLORREF m_clrFrameColor;
bool m_bMultLineTip = false; //多行不同颜色文字Tip模式标记
CString* m_values = NULL; //多行文本
int* m_colors = NULL; //多行字体颜色
int m_lineCount = 0;
CString m_fontName = "微软雅黑";
int m_fontSize = 8;
// Construction
public:
CFyToolTip();
virtual ~CFyToolTip();
public:
BOOL Create(CWnd* pWnd, BYTE bAlpha, DWORD dwStyle= WS_POPUP);
void SetToolTipSize(CSize szSize);
void ShowTips(int nX, int nY, CString strTipsContent);
void HideTips();
void SetBkColor(COLORREF clrBack);
void SetTipTextColor(COLORREF clrText);
void SetFrameColor(COLORREF clrFrame);
void ShowTips(CPoint& point, CRect& parentRect, CString* vals, int* colors, int count);
void ShowXTips(int xCenter, int yTop, CString strTipsContent);
void ShowY1Tips(int xRight, int yCenter, CString strTipsContent);
void ShowY2Tips(int xLeft, int yCenter, CString strTipsContent);
int GetTipsTextHeight();
void DrawTransparentRect(CDC* dc, CRect* rect);
protected:
//{{AFX_MSG(CFyToolTip)
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
三、透明提示框的实现
请看OnPaint函数,先重绘客户区背景和边框 --> 再画各行文本(文字、字体和颜色)。
每当调用ShowTips函数时,根据传入的显示起点和显示内容,重新计算Tip窗口的大小位置,再调用OnPaint完成重绘。
// 作者:浮云绘图,专业付费定制图形编辑器、工控曲线、报表等各类绘图软件
// QQ:316868127
BOOL CFyToolTip::Create(CWnd* pWnd, BYTE bAlpha, DWORD dwStyle)
{
LPCTSTR lpszClassName = AfxRegisterWndClass(NULL);
BOOL bRet = CreateEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, lpszClassName, _T(""), //WS_EX_TOOLWINDOW WS_EX_TOPMOST
dwStyle, 0, 0, 1, 1, pWnd->GetSafeHwnd(), NULL); // WS_POPUP WS_CHILD
if (bRet)
{
CSize sz(30,20);
MoveWindow(CRect(m_ptOrg, sz));
m_TipSize = sz;
}
LONG para = GetWindowLong(this->GetSafeHwnd(), GWL_EXSTYLE);
para |= WS_EX_LAYERED;
SetWindowLong(this->GetSafeHwnd(), GWL_EXSTYLE, para);
SetLayeredWindowAttributes(RGB(0, 0, 0), bAlpha, LWA_ALPHA); //LWA_ALPHA
m_parent = pWnd;
return bRet;
}
void CFyToolTip::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CRect rtWin;
GetClientRect(rtWin);
dc.FillSolidRect(rtWin, m_clrBack);
dc.SetBkMode(TRANSPARENT);
dc.Draw3dRect(rtWin, m_clrFrameColor, m_clrFrameColor);
CFont font;
font.CreatePointFont(m_fontSize * 10, m_fontName, NULL);
CFont* pOldFont = dc.SelectObject(&font);
CRect curLineRect;
if (!m_bMultLineTip)
{
CSize txtSize = dc.GetTextExtent(m_strTips); // 字串的长度像素
curLineRect.SetRect(rtWin.left + 0, 1, rtWin.right, txtSize.cy);
dc.SetTextColor(m_clrText);
dc.DrawText(m_strTips, rtWin, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
}
else
{
int offsetY = 1;
for (int i = 0; i < m_lineCount; i++)
{
CSize txtSize = dc.GetTextExtent(m_values[i]); // 字串的长度像素
dc.SetTextColor(m_colors[i]);
curLineRect.SetRect(rtWin.left + 4, offsetY, rtWin.right, offsetY + txtSize.cy);
dc.DrawText(m_values[i], curLineRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
offsetY += txtSize.cy + 1;
}
}
dc.SelectObject(pOldFont);
}
void CFyToolTip::ShowTips(int nX, int nY, CString strTipsContent)
{
m_bMultLineTip = false;
m_strTips = strTipsContent;
//计算字符串长宽
HDC hDC = ::GetDC(this->m_hWnd);
CDC dc;
dc.Attach(hDC);
CFont font;
font.CreatePointFont(m_fontSize * 10, m_fontName, NULL);
CFont* pOldFont = dc.SelectObject(&font);
CSize txtSize = dc.GetTextExtent(m_strTips); // 字串的长度像素
dc.SelectObject(pOldFont);
//设置窗口位置大小
m_ptOrg = CPoint(nX, nY);
m_TipSize= txtSize;
MoveWindow(nX, nY, m_TipSize.cx, m_TipSize.cy, TRUE);
if (!IsWindowVisible())
{
ShowWindow(SW_SHOW);
}
Invalidate(TRUE);
}
void CFyToolTip::ShowXTips(int xCenter, int yTop, CString strTipsContent)
{
m_bMultLineTip = false;
m_strTips = strTipsContent;
//计算字符串长宽
HDC hDC = ::GetDC(this->m_hWnd);
CDC dc;
dc.Attach(hDC);
CFont font;
font.CreatePointFont(m_fontSize * 10, m_fontName, NULL);
CFont* pOldFont = dc.SelectObject(&font);
CSize txtSize = dc.GetTextExtent(m_strTips); // 字串的长度像素
dc.SelectObject(pOldFont);
//设置窗口位置大小
int nX = xCenter - txtSize.cx / 2;
m_ptOrg = CPoint(nX, yTop);
m_TipSize = txtSize;
MoveWindow(nX, yTop, m_TipSize.cx + 2, m_TipSize.cy, TRUE);
if (!IsWindowVisible())
{
ShowWindow(SW_SHOW);
}
Invalidate(TRUE);
}
void CFyToolTip::ShowY1Tips(int xRight, int yCenter, CString strTipsContent)
{
......
}
void CFyToolTip::ShowY2Tips(int xLeft, int yCenter, CString strTipsContent)
{
......
}
void CFyToolTip::ShowTips(CPoint& point, CRect& parentRect, CString* vals, int* colors, int count)
{
......
}
void CFyToolTip::HideTips()
{
if (IsWindow(m_hWnd))
{
ShowWindow(SW_HIDE);
}
}
void CFyToolTip::SetBkColor(COLORREF clrBack)
{
m_clrBack = clrBack;
}
void CFyToolTip::SetFrameColor(COLORREF clrFrame)
{
m_clrFrameColor = clrFrame;
}
void CFyToolTip::SetTipTextColor(COLORREF clrText)
{
m_clrText = clrText;
}
.....
本文完整了实现了上图展示的3中透明信息提示框,可以满足绝大多数对透明窗口信息提示框的功能要求。如果在某些特性场景下,还需要支持FyTipTool提示框支持鼠标 点击、移动等事件,可以继续扩展。需增加以下事件支持:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseLeave();
四、CFyToolTip使用示例
//1声明变量,Y轴上左Tip
CFyToolTip* m_y1ToolTip;
//2构造变量,透明度200/255
m_y1ToolTip = new CFyToolTip();
m_y1ToolTip->Create(this, 200);
//3显示、隐藏提示框
if (GetPointInArea(point))
{
CString syTip = GetYToolTip(point);
int y1Right;
GetYAxesTipXPos(virpoint, y1Right, y2Left);
if (y1Right > 0)
{
CPoint tmpPt(y1Right, point.y);
ClientToScreen(&tmpPt);
m_y1ToolTip->ShowY1Tips(tmpPt.x, tmpPt.y, syTip);
}
}
else
{
m_y1ToolTip->HideTips();
}