一、 本文介绍一个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.}
当按钮的背景需要重画时,应用程序框架调用此函数。
其他实现细节请下载源代码。运行程序的效果图见图一、图二和图三。
from:http://www.vckbase.com/index.php/wv/8