1.自绘按钮时,首先需要按钮具有自绘能力,即要为按钮设置BS_OWNERDRAW风格。再次需要重载按钮的PreSubclassWindow虚函数,在该函数中修改按钮风格。代码如下:
void CMyButton::PreSubclassWindow() { ModifyStyle(0, BS_OWNERDRAW); /*使控件具有自绘的能力*/ /*设置按钮的有效区域*/ CRect rc; CRgn rgn; GetClientRect(&rc); /*有效区域为一个圆角矩形*/ rgn.CreateRoundRectRgn(rc.left, rc.top, rc.right, rc.bottom,5, 5); SetWindowRgn(rgn, TRUE); rgn.DeleteObject(); CButton::PreSubclassWindow(); }
2.当按钮具有自绘功能时,每次控件改变状态时都会触发DrawItem函数。因此在该函数里实现按钮绘制。
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CRect rect = lpDrawItemStruct->rcItem; CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC); int nSaveDC = pDC->SaveDC(); UINT state = lpDrawItemStruct->itemState; POINT pt; TCHAR strText[MAX_PATH + 2]; GetWindowTextW(strText, MAX_PATH); /*绘制按钮外边框*/ pt.x = 5; pt.y = 5; CPen *hOldPen = pDC->SelectObject(&m_OutsideBorder); pDC->RoundRect(&rect, pt); pDC->SelectObject(hOldPen); //获取按钮的状态 if(state & ODS_FOCUS) { m_bFocus = TRUE; m_bSelected = TRUE; } else { m_bFocus = FALSE; m_bSelected = FALSE; } if(state & ODS_SELECTED || state & ODS_DEFAULT) { m_bFocus = TRUE; } /*根据按钮的状态填充按钮的底色*/ CBrush *pOldBrush; if(m_bOver) { pOldBrush = pDC->SelectObject(&m_FillActive); DoGradientFill(pDC, &rect); } else { pOldBrush = pDC->SelectObject(&m_FillInactive); DoGradientFill(pDC, &rect); } pDC->SelectObject(pOldBrush); /*根据按钮状态绘制内边框*/ if(m_bOver || m_bSelected) { DrawInsideBorder(pDC, &rect); } /*绘制按钮的文本*/ if(strText != NULL) { CFont *hFont = GetFont(); CFont *hOldFont = pDC->SelectObject(hFont); CSize szExtent = pDC->GetTextExtent(strText, lstrlen(strText)); CPoint pt(rect.CenterPoint().x - szExtent.cx / 2, rect.CenterPoint().y - szExtent.cy / 2); int nMode = pDC->SetBkMode(TRANSPARENT); if(state & ODS_SELECTED) pt.Offset(1, 1); if(state & ODS_SELECTED) pDC->DrawState(pt, szExtent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL); else pDC->DrawState(pt, szExtent, strText, DSS_NORMAL, TRUE, 0, (HBRUSH)NULL); pDC->SelectObject(hOldFont); pDC->SetBkMode(nMode); } pDC->RestoreDC(nSaveDC); }
3.一般都会为按钮定义几种不同状态时的外观,比如光标滑过时的状态,按钮按下时的状态,按钮禁用时的状态,以及按钮的正常状 态等等。这就要为新的按钮添加几种重要的消息响应。比如WM_MOUSELEAVE消息,WM_MOUSEHOVER消息和WM_MOUSEMOVE消息 等等,值得一提的是前两个消息的响应函数需要自己手动添加,微软提供了一个TrackMouseEvent函数在光标离开一个窗口时投递 WM_MOUSELEAVE消息,光标滑过窗口时投递WM_MOUSEHOVER消息。一般来说可以在WM_MOUSEMOVE消息响应函数中调用 TrackMouseEvent函数来投递WM_MOUSELEAVE消息和WM_MOUSEHOVER消息。然后在WM_MOUSELEAVE消息的响 应函数中标记“光标已经离开按钮”,然后调用InvalidateRect函数让按钮重绘。在WM_MOUSEHOVER消息的响应函数中标记“光标正在 按钮上方”,并调用InvalidateRect函数让按钮重绘。
按钮几种状态:
Normal状态,就是按钮一开始显示时的样子。
Over状态,鼠标指针移动到按钮上面时按钮显示的样子。
Down状态,按下按钮时显示的样子。
Focus状态,按钮按下后松开的样子,例如标准按钮按下松开之后会看到按钮内部有一个虚线框。
Disable状态,当然就是按钮被设置成无效的时候的样子啦。
定义按钮状态变量:
/*按钮的状态*/ BOOL m_bOver; //鼠标位于按钮之上时该值为true,反之为false BOOL m_bTracking; //鼠标按下没有释放是该值为true BOOL m_bSelected; //按钮被按下是该值为true BOOL m_bFocus; //按钮为当前焦点时该值为true
响应按钮消息:
void CMyButton::OnMouseMove(UINT nFlags, CPoint point) { if(!m_bTracking) { TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.hwndTrack = m_hWnd; tme.dwFlags = TME_LEAVE | TME_HOVER; tme.dwHoverTime = 1; m_bTracking = _TrackMouseEvent(&tme); } CButton::OnMouseMove(nFlags, point); } void CMyButton::OnMouseHover(UINT nFlags, CPoint point) { m_bOver = TRUE; InvalidateRect(NULL); CButton::OnMouseHover(nFlags, point); } void CMyButton::OnMouseLeave() { m_bOver = FALSE; m_bTracking = FALSE; InvalidateRect(NULL, FALSE); CButton::OnMouseLeave(); }
4.为父窗口响应按钮通告消息在父窗口的消息映射里添加消息映射宏:
ON_BN_CLICKED(IDC_BUTTON1, OnBtnClick)
ON_BN_DOUBLECLICKED(IDC_BUTTON1, OnBtnDClick)
声明和定义消息映射函数
public: // 响应按钮单击事件 afx_msg void OnBtnClick(void); // 响应按钮双击事件 afx_msg void OnBtnDClick(void);
// 响应按钮单击事件 void CButtonOwnerDraw_0200Dlg::OnBtnClick(void) { MessageBox(_T("按钮被单击!")); } // 响应按钮双击事件 void CButtonOwnerDraw_0200Dlg::OnBtnDClick(void) { MessageBox(_T("按钮被双击!")); }
参考:
http://c.chinaitlab.com/vc/828932.html
http://www.vckbase.com/index.php/wv/374.html
http://www.360doc.com/content/10/1128/14/2226925_73128026.shtml
代码:
http://pan.baidu.com/share/link?shareid=452609&uk=2600520119