自定义按钮的实现,说到底还是重载Create,以及继承CButton类。然后利用presubclasswindow,drawitem等函数。
下面,我们就看看详细的代码:
首先我们在派生自CButton类的CMybutton类头文件中定义代码:
BOOL Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID);
//默认颜色及边框的按钮创建函数
BOOL Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID, COLORREF color);
//创建具有选中态及悬挂态颜色的按钮
BOOL Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID, UINT nBorder, COLORREF nColor, COLORREF sColor, COLORREF hColor, COLORREF dColor);
// 完全指定属性的按钮创建函数
// lpszCaption - 按钮标题
// dwStyle - 按钮样式
// point - 按钮在父窗体上的坐标
// hRgn - 按钮的路径
// pParentWnd - 父窗体
// nID - 控件ID
// nBorder - 3D边框的宽度(可以是1或2(像素))
// nColor - 正常状态的颜色
// sColor - 选中状态的颜色
// hColor - 悬挂状态的颜色
// dColor - 禁用状态的颜色
UINT m_nBorder; //边框的宽度
LONG m_lfEscapement; //标题的位置
COLORREF m_nColor; //普通状态时按钮的背景色
COLORREF m_sColor; //选中状态时按钮的背景色
COLORREF m_hColor; //悬挂状态时按钮的背景色
COLORREF m_dColor; //禁用状态时按钮的背景色
CBitmap * m_pNormal; //普通状态时按钮的位图
CBitmap * m_pSelected; //选中状态时按钮的位图
CBitmap * m_pHover; //悬挂状态时按钮的位图
CBitmap * m_pDisabled; //禁用状态时按钮的位图
CPoint m_CenterPoint; //标题的坐标中点
BOOL m_bMouseDown; //标识鼠标按下
BOOL m_bHover; //标识鼠标经过按钮
BOOL m_bCapture; //标识鼠标被捕获
HRGN m_hRgn; //按钮路径
BOOL m_bNeedBitmaps; //标识状态位图是否重绘
void DrawButton(CDC * pDC, CRect * pRect, UINT state); //将位图绘制到屏幕上
void PrepareStateBitmaps(CDC * pDC, CRect * pRect); //准备按钮状态位图
BOOL HitTest(CPoint point); //判断鼠标是否在按钮区域内
// void RgnPixelWork(CDC * pDC, CRgn * pRgn); // region pixel work - unused
void FrameRgn3D(HDC hDC, const HRGN hRgn, BOOL bSunken); //绘制3D边框
void CheckHover(CPoint point); //检测鼠标是否经过按钮
protected:
void PrepareNormalState(CDC * pDC, CDC * pMemDC, CRect * pRect); //准备按钮普通状态时的位图
void PrepareSelectedState(CDC * pDC, CDC * pMemDC, CRect * pRect); //准备按钮被选中状态时的位图
void PrepareHoverState(CDC * pDC, CDC * pMemDC, CRect * pRect); //准备按钮悬挂状态时的位图
void PrepareDisabledState(CDC * pDC, CDC * pMemDC, CRect * pRect); //准备按钮禁用状态时的位图
void DrawButtonCaption(HDC hDC, CRect * pRect, BOOL bEnabled, BOOL bSunken); //绘制按钮标题
void PaintRgn(CDC * pDC, CDC * pMemDC, CBitmap * pBitmap, COLORREF color, CRect * pRect, BOOL bEnabled, BOOL bSunken); //绘制按钮
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL
protected:
//{{AFX_MSG(CMybutton)
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
在CMybutton的实现文件中:CMybutton::CMybutton()
{
m_nColor = GetSysColor(COLOR_BTNFACE);
m_sColor = m_nColor;
m_hColor = m_nColor;
m_dColor = m_nColor;
m_nBorder = 1;
m_pNormal = NULL;
m_pSelected = NULL;
m_pHover = NULL;
m_pDisabled = NULL;
m_hRgn = 0;
m_bHover = false;
m_bCapture = false;
m_bMouseDown = false;
m_bNeedBitmaps = true;
}
CMybutton::~CMybutton()
{
delete m_pNormal;
delete m_pSelected;
delete m_pHover;
delete m_pDisabled;
DeleteObject(m_hRgn);
}
BOOL CMybutton::Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID)
{
// store region in member variable
DeleteObject(m_hRgn);
m_hRgn = CreateRectRgn(0, 0, 31, 31);
CRect box(0, 0, 0, 0);
if (m_hRgn != 0)
CombineRgn(m_hRgn, hRgn, 0, RGN_COPY);
// make sure that region bounding rect is located in (0, 0)
GetRgnBox(m_hRgn, &box);
OffsetRgn(m_hRgn, -box.left, -box.top);
GetRgnBox(m_hRgn, &box);
// update position of region center for caption output
m_CenterPoint = CPoint(box.left + box.Width() /2 , box.top + box.Height() /2);
box.OffsetRect(point);
return CButton::Create(lpszCaption, dwStyle, box, pParentWnd, nID);
}
BOOL CMybutton::Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID, COLORREF color)
{
m_sColor = color;
m_hColor = color;
// call another constructor
return Create(lpszCaption, dwStyle, point, hRgn, pParentWnd, nID);
}
BOOL CMybutton::Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID, UINT nBorder, COLORREF nColor, COLORREF sColor, COLORREF hColor, COLORREF dColor)
{
// change default colors
m_nBorder = nBorder;
m_nColor = nColor;
m_sColor = sColor;
m_hColor = hColor;
m_dColor = hColor;
// call another constructor
return Create(lpszCaption, dwStyle, point, hRgn, pParentWnd, nID);
}
void CMybutton::PreSubclassWindow()
{
// change window style to allow owner draw
ModifyStyle(0, BS_OWNERDRAW | BS_PUSHBUTTON);
CButton::PreSubclassWindow();
}
int CMybutton::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CButton::OnCreate(lpCreateStruct) == -1)
return -1;
// assign new region to a window
m_bNeedBitmaps = true;
SetWindowRgn(m_hRgn, true);
return 0;
}
void CMybutton::OnMouseMove(UINT nFlags, CPoint point)
{
// Test if mouse is above the button.
if (!m_bMouseDown)
CheckHover(point);
CButton::OnMouseMove(nFlags, point);
}
void CMybutton::CheckHover(CPoint point)
{
if (HitTest(point)) {
if (!m_bCapture) {
SetCapture();
m_bCapture = true;
}
if (!m_bHover) {
m_bHover = true;
RedrawWindow();
}
}
else {
if (m_bCapture) {
ReleaseCapture();
m_bCapture = false;
}
m_bHover = false;
RedrawWindow();
}
}
BOOL CMybutton::HitTest(CPoint point)
{
BOOL result = false;
// Obtain handle to window region.
HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hRgn);
CRect rgnRect;
GetRgnBox(hRgn, &rgnRect);
// First check if point is in region bounding rect.
// Then check if point is in the region in adition to being in the bouding rect.
result = PtInRect(&rgnRect, point)&& PtInRegion(hRgn, point.x, point.y);
// Clean up and exit.
DeleteObject(hRgn);
return result;
}
BOOL CMybutton::OnEraseBkgnd(CDC* pDC)
{
// do not erase background
return 1;
}
//////////////////////// DRAWING ROUTINES ////////////////////////////
void CMybutton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// prepare DC
CDC* pDC = CDC::FromHandle(lpDrawItemStruct -> hDC);
CRect rect;
GetClientRect(rect);
// prepare bitmaps they need to be prepared
if (m_bNeedBitmaps)
PrepareStateBitmaps(pDC, &rect);
// draw button to the screen
DrawButton(pDC, &rect, lpDrawItemStruct -> itemState);
}
void CMybutton::PrepareStateBitmaps(CDC * pDC, CRect * pRect)
{
// 准备DC
CDC * pMemDC;
pMemDC = new CDC;
pMemDC -> CreateCompatibleDC(pDC);
//为按钮的各种状态准备位图
PrepareNormalState(pDC, pMemDC, pRect);
PrepareSelectedState(pDC, pMemDC, pRect);
PrepareHoverState(pDC, pMemDC, pRect);
PrepareDisabledState(pDC, pMemDC, pRect);
// 释放资源
delete pMemDC;
m_bNeedBitmaps = false;
}
void CMybutton::PrepareNormalState(CDC * pDC, CDC * pMemDC, CRect * pRect)
{
// prepare MYBS_NORMAL state bitmap
delete m_pNormal;
m_pNormal = new CBitmap;
PaintRgn(pDC, pMemDC, m_pNormal, m_nColor, pRect, true, false);
}
void CMybutton::PrepareSelectedState(CDC * pDC, CDC * pMemDC, CRect * pRect)
{
// prepare MYBS_SELECTED state bitmap
delete m_pSelected;
m_pSelected = new CBitmap;
PaintRgn(pDC, pMemDC, m_pSelected, m_sColor, pRect, true, true);
}
void CMybutton::PrepareHoverState(CDC * pDC, CDC * pMemDC, CRect * pRect)
{
// prepare MYBS_HOVER state bitmap
delete m_pHover;
m_pHover = new CBitmap;
PaintRgn(pDC, pMemDC, m_pHover, m_hColor, pRect, true, false);
}
void CMybutton::PrepareDisabledState(CDC * pDC, CDC * pMemDC, CRect * pRect)
{
// prepare MYBS_DISABLED state bitmap
delete m_pDisabled;
m_pDisabled = new CBitmap;
PaintRgn(pDC, pMemDC, m_pDisabled, m_dColor, pRect, false, false);
}
void CMybutton::PaintRgn(CDC * pDC, CDC * pMemDC, CBitmap * pBitmap, COLORREF color, CRect * pRect, BOOL bEnabled, BOOL bSunken)
{
// 创建位图
pBitmap -> CreateCompatibleBitmap(pDC, pRect -> Width(), pRect -> Height());
CBitmap * pOldBitmap = pMemDC -> SelectObject(pBitmap);
// 准备路径
HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hRgn);
// 用传进来的颜色填充按钮路径区域
HBRUSH hBrush = CreateSolidBrush(color);
pMemDC -> FillSolidRect(pRect, RGB(0, 0, 0));
FillRgn(pMemDC -> GetSafeHdc(), hRgn, hBrush);
DeleteObject(hBrush);
// 绘制按钮标题及3D边框
DrawButtonCaption(pMemDC -> GetSafeHdc(), pRect, bEnabled, bSunken);
FrameRgn3D(pMemDC -> GetSafeHdc(), hRgn, bSunken);
// 释放资源
DeleteObject(hRgn);
pMemDC -> SelectObject(pOldBitmap);
}
void CMybutton::DrawButtonCaption(HDC hDC, CRect * pRect, BOOL bEnabled, BOOL bSunken)
{
//选择父窗体的字体
int nOldMode = SetBkMode(hDC, TRANSPARENT);
CString text;
GetWindowText(text);
LOGFONT lf;
GetParent() -> GetFont() -> GetLogFont(&lf);
HFONT hFont = CreateFontIndirect(&lf);
HFONT hOldFont = (HFONT) SelectObject(hDC, hFont);
// 计算表条输出的位置
TEXTMETRIC tm;
GetTextMetrics(hDC, &tm);
CPoint p = CPoint(m_CenterPoint.x, m_CenterPoint.y + tm.tmHeight/ 2);
if (bSunken)
p.Offset(m_nBorder, m_nBorder);
//根据按钮的状态绘制按钮标题
if (bEnabled )
{
if(m_bHover || m_bCapture)
{
SetTextColor(hDC, GetSysColor(COLOR_BTNTEXT));
SetTextAlign(hDC, TA_CENTER | TA_BOTTOM);
TextOut(hDC, p.x, p.y, text, text.GetLength());
}
else
{
SetTextColor(hDC, GetSysColor(COLOR_3DHILIGHT));
SetTextAlign(hDC, TA_CENTER | TA_BOTTOM);
TextOut(hDC, p.x, p.y, text, text.GetLength());
}
}
else {
SetTextColor(hDC, GetSysColor(COLOR_3DHILIGHT));
TextOut(hDC, p.x + 1, p.y + 1, text, text.GetLength());
SetTextColor(hDC, GetSysColor(COLOR_3DSHADOW));
TextOut(hDC, p.x, p.y, text, text.GetLength());
}
SelectObject(hDC, hOldFont);
DeleteObject(hFont);
SetBkMode(hDC, nOldMode);
}
void CMybutton::FrameRgn3D(HDC hDC, const HRGN hRgn, BOOL bSunken)
{
// we need two differenr regions to keep base region and border region
HBRUSH hBrush;
HRGN hBaseRgn = CreateRectRgn(0, 0, 0, 0);
COLORREF ltOuter; //左上外侧边框的颜色
COLORREF ltInner; //左上内存边框的颜色
COLORREF rbOuter; //右下外侧边框的颜色
COLORREF rbInner; //右下内侧边框的颜色
//边框颜色配置
if (!bSunken) {
ltOuter = GetSysColor(COLOR_3DLIGHT);
ltInner = GetSysColor(COLOR_3DHILIGHT);
rbOuter = GetSysColor(COLOR_3DDKSHADOW);
rbInner = GetSysColor(COLOR_3DSHADOW);
}
else {
rbInner = GetSysColor(COLOR_3DLIGHT);
rbOuter = GetSysColor(COLOR_3DHILIGHT);
ltInner = GetSysColor(COLOR_3DDKSHADOW);
ltOuter = GetSysColor(COLOR_3DSHADOW);
}
// 移动3D边框,将其与基本区域分离
switch (m_nBorder)
{
case 2:
CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
OffsetRgn(hBaseRgn, 2, 2);
CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
hBrush = CreateSolidBrush(ltInner);
FillRgn(hDC, hBaseRgn, hBrush);
DeleteObject(hBrush);
CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
OffsetRgn(hBaseRgn, -2, -2);
CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
hBrush = CreateSolidBrush(rbInner);
FillRgn(hDC, hBaseRgn, hBrush);
DeleteObject(hBrush);
CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
OffsetRgn(hBaseRgn, 1, 1);
CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
hBrush = CreateSolidBrush(ltOuter);
FillRgn(hDC, hBaseRgn, hBrush);
DeleteObject(hBrush);
CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
OffsetRgn(hBaseRgn, -1, -1);
CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
hBrush = CreateSolidBrush(rbOuter);
FillRgn(hDC, hBaseRgn, hBrush);
DeleteObject(hBrush);
break;
default:
CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
OffsetRgn(hBaseRgn, 1, 1);
CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
hBrush = CreateSolidBrush(ltInner);
FillRgn(hDC, hBaseRgn, hBrush);
DeleteObject(hBrush);
CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
OffsetRgn(hBaseRgn, -1, -1);
CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);
hBrush = CreateSolidBrush(rbOuter);
FillRgn(hDC, hBaseRgn, hBrush);
DeleteObject(hBrush);
break;
}
//释放资源
DeleteObject(hBaseRgn);
}
void CMybutton::DrawButton(CDC * pDC, CRect * pRect, UINT state)
{
// 创建内存设备
CDC * pMemDC = new CDC;
pMemDC -> CreateCompatibleDC(pDC);
CBitmap * pOldBitmap;
//获取按钮路径
HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hRgn);
//根据按钮状态为按钮选择状态位图
if (state & ODS_DISABLED)
pOldBitmap = pMemDC -> SelectObject(m_pDisabled);
else {
if (state & ODS_SELECTED)
pOldBitmap = pMemDC -> SelectObject(m_pSelected);
else {
if (m_bHover)
pOldBitmap = pMemDC -> SelectObject(m_pHover);
else
pOldBitmap = pMemDC -> SelectObject(m_pNormal);
}
}
//绘制按钮的裁剪区域
::SelectClipRgn(pDC -> GetSafeHdc(), hRgn);
pDC -> BitBlt(0, 0, pRect -> Width(), pRect -> Height(), pMemDC, 0, 0, SRCCOPY);
::SelectClipRgn(pDC -> GetSafeHdc(), NULL);
//释放资源
DeleteObject(hRgn);
pMemDC -> SelectObject(pOldBitmap);
delete pMemDC;
最后看看实现结果:
这就是我们自定义的按钮了。