一、 本文介绍一个CButton的派生类CLinkButton,用此派生类制作的按钮具有以下特点:
1、按钮的外观类似静态控件类CStatic 产生的对象。(参见图一)
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)
图一
2、当鼠标的光标移到按钮上,但并未按下时,光标改变形状,字体改变形状;按钮类似应用在工具条和菜单上的扁平钮效果。(参见图二)
图二
3、当按钮按下的情形:(参见图三)
图三
二、下面具体描述这种按钮的实现方法和步骤:
在VC6的IDE环境中,生成一个基于对话框的PROJECT。
将对话框资源中按钮的属性页打开,在“Style”标签页中选取按钮的“Owner Draw”(自绘)属性。
将光标引入到应用程序的资源中。
利用CLASSWIZARD,用CButton为基类,派生一个新类:CLinkButton。
在派生类中重载基类CButton的虚函数: virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
之所以要重载这个函数是因为选择了按钮的 “Owner Draw”属性后,当按钮的可视行为发生变化时,应用程序的框架要调用这个函数来重新绘制按钮。
定制以下的消息处理:afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
声明类成员变量定义: //定义字体变量
CFont fUnderline;
//定义光标变量
HCURSOR hHand;
//决定按钮是否按下
bool bLBtnDown;
//决定鼠标是否在按钮上
bool bHighlight;
三、 派生类CLinkButton 的具体实现:
1、重载函数 DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)。 void CLinkButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// 获取一个CDC指针
CDC* pDC = CDC::FromHandle(lpDrawItemStruct-hDC);
//定义按钮区域并初始化
CRect rect(lpDrawItemStruct-rcItem);
//设置背景模式
COLORREF oc = pDC-GetTextColor();
int iObk = pDC-SetBkMode(TRANSPARENT);
//初始化按钮状态
UINT state = lpDrawItemStruct-itemState;
CFont * pOldFont = NULL;
int iYOffset = 0, iXOffset = 0;
CString strText;
GetWindowText(strText);
rect.top += iYOffset;
rect.left += iXOffset;
if (state & ODS_DISABLED)
{
//按钮置灰(DISABLED)
CBrush grayBrush;
grayBrush.CreateSolidBrush (GetSysColor (COLOR_GRAYTEXT));
CSize sz = pDC-GetTextExtent(strText);
int x = rect.left + (rect.Width() - sz.cx)/2;
int y = rect.top + (rect.Height() - sz.cy)/2;
rect.top += 2;
rect.left += 2;
pDC-SetTextColor(GetSysColor(COLOR_3DHIGHLIGHT));
pDC-DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
rect.top -= 2;
rect.left -= 2;
pDC-SetTextColor(GetSysColor(COLOR_GRAYTEXT));
pDC-DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
else
{
if (bHighlight)//光标在按钮上
{
if (state & ODS_SELECTED)
//按下按钮
pDC-Draw3dRect(rect,GetSysColor(COLOR_3DSHADOW), GetSysColor(COLOR_3DHILIGHT));
else
//未按下按钮
pDC-Draw3dRect(rect,GetSysColor(COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
//字体颜色
pDC-SetTextColor(RGB(0,0,255));
//加下画线(也可以用其他字体)
if (fUnderline.GetSafeHandle() == NULL)
{
CFont * pFont = GetFont();
ASSERT(pFont);
LOGFONT lf;
pFont-GetLogFont(&lf);
lf.lfUnderline = TRUE;
fUnderline.CreateFontIndirect(&lf);
}
pOldFont = pDC-SelectObject(&fUnderline);
}
else pDC-SetTextColor(GetSysColor(COLOR_BTNTEXT));
pDC-DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
if (pOldFont) pDC-SelectObject(pOldFont);
}
}2、定制的消息处理函数 void CLinkButton::OnMouseMove(UINT nFlags, CPoint point)
{
//设置一个定时器
SetTimer(1,10,NULL);
CButton::OnMouseMove(nFlags, point);
} 当鼠标光标移到按钮上时,执行此函数,定时器将发送一个 WM_TIMER消息到消息队列。由OnTimer(UINT nIDEvent)函数处理这个消息。 void CLinkButton::OnTimer(UINT nIDEvent)
{
static bool pPainted = false;
POINT pt;
GetCursorPos(&pt);
CRect rect;
GetWindowRect (rect);
if (bLBtnDown)
{
KillTimer (1);
if (pPainted) InvalidateRect (NULL);
pPainted = FALSE;
return;
}
if (!rect.PtInRect (pt))
{
bHighlight = false;
KillTimer (1);
if (pPainted)
InvalidateRect(NULL);
pPainted = false;
return;
}
else
{
bHighlight = true;
if (!pPainted)
{
pPainted = true;
InvalidateRect(NULL);
}
}
CButton::OnTimer(nIDEvent);
}
BOOL CLinkButton::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (bHighlight)
{
::SetCursor(hHand);
return true;
}
return CButton::OnSetCursor(pWnd, nHitTest, message);
}
int CLinkButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CButton::OnCreate(lpCreateStruct) == -1)
return -1;
CFont * pFont = GetFont();
ASSERT(pFont);
LOGFONT lf;
pFont-GetLogFont(&lf);
lf.lfUnderline = TRUE;
fUnderline.CreateFontIndirect(&lf);
return 0;
} 这个函数由框架在显示出按钮之前自动调用,我在这里初始化按钮上显示的字体。 void CLinkButton::OnLButtonUp(UINT nFlags, CPoint point)
{
bLBtnDown = false;
if (bHighlight)
{
bHighlight = false;
InvalidateRect(NULL);
}
CButton::OnLButtonUp(nFlags, point);
} 当按下按钮又放开时调用这个函数。 void CLinkButton::OnLButtonDown(UINT nFlags, CPoint point)
{
bLBtnDown = true;
CButton::OnLButtonDown(nFlags, point);
} 当按下按钮时调用这个函数。 BOOL CLinkButton::OnEraseBkgnd(CDC* pDC)
{
COLORREF cr = GetSysColor(COLOR_3DFACE);
int r = GetRValue(cr);
int g = GetGValue(cr);
int b = GetBValue(cr);
if (r 1) r -= 2;
if (g 1) g -= 2;
if (r 3 && g 3 && b 253) b += 2;
COLORREF cr1 = RGB(r,g,b);
CRect rc;
GetClientRect(rc);
pDC-FillSolidRect(rc, cr1);
return CButton::OnEraseBkgnd(pDC);
} 当按钮的背景需要重画时,应用程序框架调用此函数。
其他实现细节请下载源代码。运行程序的效果图见图一、图二和图三。