一、 本文介绍一个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. else37. { 38. if (bHighlight)//光标在按钮上 39. { 40. if (state & ODS_SELECTED) 41. //按下按钮 42. pDC->Draw3dRect(rect,GetSysColor(COLOR_3DSHADOW), GetSysColor(COLOR_3DHILIGHT)); 43. else44. //未按下按钮 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. else28. { 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.}当按钮的背景需要重画时,应用程序框架调用此函数。
其他实现细节请下载源代码。运行程序的效果图见图一、图二和图三。