一、 本文介绍一个CButton的派生类CLinkButton,用此派生类制作的按钮具有以下特点:
1、按钮的外观类似静态控件类CStatic 产生的对象。(参见图一)
图一
2、当鼠标的光标移到按钮上,但并未按下时,光标改变形状,字体改变形状;按钮类似应用在工具条和菜单上的扁平钮效果。(参见图二)
图二
3、当按钮按下的情形:(参见图三)
图三
二、下面具体描述这种按钮的实现方法和步骤:
在VC6的IDE环境中,生成一个基于对话框的PROJECT。 将对话框资源中按钮的属性页打开,在“Style”标签页中选取按钮的“Owner Draw”(自绘)属性。 将光标引入到应用程序的资源中。 利用CLASSWIZARD,用CButton为基类,派生一个新类:CLinkButton。 在派生类中重载基类CButton的虚函数:1.
virtual
void
DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
之所以要重载这个函数是因为选择了按钮的 “Owner Draw”属性后,当按钮的可视行为发生变化时,应用程序的框架要调用这个函数来重新绘制按钮。
定制以下的消息处理:1.
afx_msg
void
OnMouseMove(
UINT
nFlags, CPoint point);
2.
afx_msg
BOOL
OnSetCursor(CWnd* pWnd,
UINT
nHitTest,
UINT
message);
3.
afx_msg
void
OnTimer(
UINT
nIDEvent);
4.
afx_msg
void
OnLButtonUp(
UINT
nFlags, CPoint point);
5.
afx_msg
void
OnLButtonDown(
UINT
nFlags, CPoint point);
6.
afx_msg
int
OnCreate(LPCREATESTRUCT lpCreateStruct);
7.
afx_msg
BOOL
OnEraseBkgnd(CDC* pDC);
1.
//定义字体变量
2.
CFont fUnderline;
3.
//定义光标变量
4.
HCURSOR
hHand;
5.
//决定按钮是否按下
6.
bool
bLBtnDown;
7.
//决定鼠标是否在按钮上
8.
bool
bHighlight;
三、 派生类CLinkButton 的具体实现:
1、重载函数 DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)。
01.
void
CLinkButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
02.
{
03.
// 获取一个CDC指针
04.
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
05.
//定义按钮区域并初始化
06.
CRect rect(lpDrawItemStruct->rcItem);
07.
//设置背景模式
08.
COLORREF
oc = pDC->GetTextColor();
09.
int
iObk = pDC->SetBkMode(TRANSPARENT);
10.
//初始化按钮状态
11.
UINT
state = lpDrawItemStruct->itemState;
12.
CFont * pOldFont = NULL;
13.
int
iYOffset = 0, iXOffset = 0;
14.
CString strText;
15.
GetWindowText(strText);
16.
rect.top += iYOffset;
17.
rect.left += iXOffset;
18.
19.
if
(state & ODS_DISABLED)
20.
{
21.
//按钮置灰(DISABLED)
22.
CBrush grayBrush;
23.
grayBrush.CreateSolidBrush (GetSysColor (COLOR_GRAYTEXT));
24.
CSize sz = pDC->GetTextExtent(strText);
25.
int
x = rect.left + (rect.Width() - sz.cx)/2;
26.
int
y = rect.top + (rect.Height() - sz.cy)/2;
27.
rect.top += 2;
28.
rect.left += 2;
29.
pDC->SetTextColor(GetSysColor(COLOR_3DHIGHLIGHT));
30.
pDC->DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
31.
rect.top -= 2;
32.
rect.left -= 2;
33.
pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
34.
pDC->DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
35.
}
36.
else
37.
{
38.
if
(bHighlight)
//光标在按钮上
39.
{
40.
if
(state & ODS_SELECTED)
41.
//按下按钮
42.
pDC->Draw3dRect(rect,GetSysColor(COLOR_3DSHADOW), GetSysColor(COLOR_3DHILIGHT));
43.
else
44.
//未按下按钮
45.
pDC->Draw3dRect(rect,GetSysColor(COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
46.
47.
//字体颜色
48.
pDC->SetTextColor(RGB(0,0,255));
49.
50.
//加下画线(也可以用其他字体)
51.
if
(fUnderline.GetSafeHandle() == NULL)
52.
{
53.
CFont * pFont = GetFont();
54.
ASSERT(pFont);
55.
LOGFONT lf;
56.
pFont->GetLogFont(&lf);
57.
lf.lfUnderline = TRUE;
58.
fUnderline.CreateFontIndirect(&lf);
59.
}
60.
pOldFont = pDC->SelectObject(&fUnderline);
61.
}
62.
else
pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
63.
64.
pDC->DrawText(strText, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
65.
66.
if
(pOldFont) pDC->SelectObject(pOldFont);
67.
}
68.
}
2、定制的消息处理函数
1.
void
CLinkButton::OnMouseMove(
UINT
nFlags, CPoint point)
2.
{
3.
//设置一个定时器
4.
SetTimer(1,10,NULL);
5.
6.
CButton::OnMouseMove(nFlags, point);
7.
}
当鼠标光标移到按钮上时,执行此函数,定时器将发送一个 WM_TIMER消息到消息队列。由OnTimer(UINT nIDEvent)函数处理这个消息。
01.
void
CLinkButton::OnTimer(
UINT
nIDEvent)
02.
{
03.
static
bool
pPainted =
false
;
04.
POINT pt;
05.
GetCursorPos(&pt);
06.
CRect rect;
07.
GetWindowRect (rect);
08.
if
(bLBtnDown)
09.
{
10.
KillTimer (1);
11.
if
(pPainted) InvalidateRect (NULL);
12.
pPainted = FALSE;
13.
return
;
14.
}
15.
16.
if
(!rect.PtInRect (pt))
17.
{
18.
bHighlight =
false
;
19.
KillTimer (1);
20.
21.
if
(pPainted)
22.
InvalidateRect(NULL);
23.
24.
pPainted =
false
;
25.
return
;
26.
}
27.
else
28.
{
29.
bHighlight =
true
;
30.
if
(!pPainted)
31.
{
32.
pPainted =
true
;
33.
InvalidateRect(NULL);
34.
}
35.
}
36.
37.
CButton::OnTimer(nIDEvent);
38.
}
39.
40.
41.
BOOL
CLinkButton::OnSetCursor(CWnd* pWnd,
UINT
nHitTest,
UINT
message)
42.
{
43.
if
(bHighlight)
44.
{
45.
::SetCursor(hHand);
46.
return
true
;
47.
}
48.
49.
return
CButton::OnSetCursor(pWnd, nHitTest, message);
50.
}
51.
52.
53.
int
CLinkButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
54.
{
55.
if
(CButton::OnCreate(lpCreateStruct) == -1)
56.
return
-1;
57.
58.
CFont * pFont = GetFont();
59.
ASSERT(pFont);
60.
61.
LOGFONT lf;
62.
pFont->GetLogFont(&lf);
63.
lf.lfUnderline = TRUE;
64.
65.
fUnderline.CreateFontIndirect(&lf);
66.
67.
return
0;
68.
}
这个函数由框架在显示出按钮之前自动调用,我在这里初始化按钮上显示的字体。
01.
void
CLinkButton::OnLButtonUp(
UINT
nFlags, CPoint point)
02.
{
03.
bLBtnDown =
false
;
04.
if
(bHighlight)
05.
{
06.
bHighlight =
false
;
07.
InvalidateRect(NULL);
08.
}
09.
10.
CButton::OnLButtonUp(nFlags, point);
11.
}
当按下按钮又放开时调用这个函数。
1.
void
CLinkButton::OnLButtonDown(
UINT
nFlags, CPoint point)
2.
{
3.
bLBtnDown =
true
;
4.
5.
CButton::OnLButtonDown(nFlags, point);
6.
}
当按下按钮时调用这个函数。
01.
BOOL
CLinkButton::OnEraseBkgnd(CDC* pDC)
02.
{
03.
COLORREF
cr = GetSysColor(COLOR_3DFACE);
04.
int
r = GetRValue(cr);
05.
int
g = GetGValue(cr);
06.
int
b = GetBValue(cr);
07.
if
(r > 1) r -= 2;
08.
if
(g > 1) g -= 2;
09.
if
(r < 3 && g < 3 && b < 253) b += 2;
10.
COLORREF
cr1 = RGB(r,g,b);
11.
CRect rc;
12.
GetClientRect(rc);
13.
pDC->FillSolidRect(rc, cr1);
14.
15.
return
CButton::OnEraseBkgnd(pDC);
16.
}
当按钮的背景需要重画时,应用程序框架调用此函数。
其他实现细节请下载源代码。运行程序的效果图见图一、图二和图三。