按钮这个控件在软件中使用是非常广泛的,特别是基于MFC开发,按钮提供了很好的便捷。
但是在eVC下的按钮显示效果是非常土的,且背景颜色不好改。如果裁系统的时候将XP风格裁进去的话,默认按钮效果会好一些,但是背景颜色不好控制,在ce平台下,没有鼠标是很正常的,带XP风格的CE系统按钮提示不够明显,这个时候就需要自绘按钮了。
首先,先建立一个基于CWnd的类,如:CMyButton : public CWnd
1、添加成员变量:
CDC m_dcBack;
CBitmap m_bmpBack; //背景
COLORREF m_clNorBack; //正常时背景颜色
COLORREF m_clFocBack; //设上焦点时背景颜色
COLORREF m_clNorText;
COLORREF m_clFocText;
bool m_bLastState; //最后一次状态,true =焦点
CString m_strTitleInfo; //按钮上字符
2、添加成员函数:
void InitMyButton(); //初始化按钮
void DrawBackForNor(); //默认
void DrawBackForFoc(); //按下去
void DrawBackForMid(); //中间
BOOL CreateBTN(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect,
CWnd* pParentWnd, UINT nID,LPDealWithBTN lpDealWithBTN = NULL);//创建
void SetWindowText(LPCTSTR lpszString);//重载函数
BOOL EnableWindow(BOOL bEnable = TRUE);//重载函数
3、在View里添加消息: 如果考虑到右键效果,可以参考LBUTTONDOWN()添加
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_ENABLE()
上面的工作已经将基本工作完成了。接下来就是完成相应的代码了:
void
CMyButton::OnPaint()
...
{
// CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CRect rectClient;
GetClientRect(rectClient);
CDC memDC ;
CBitmap memBitmap ;
CBitmap* oldBitmap ;
memDC.CreateCompatibleDC(&dc) ;
memBitmap.CreateCompatibleBitmap(&dc, rectClient.Width(), rectClient.Height()) ;
oldBitmap = (CBitmap *)memDC.SelectObject(&memBitmap) ;
//将m_dcCoor和m_dcLine绘制到控件上
if (memDC.GetSafeHdc() != NULL)
...{
memDC.BitBlt(0, 0, rectClient.Width(), rectClient.Height(),
&m_dcBack, 0, 0, SRCCOPY) ;
dc.BitBlt(0, 0, rectClient.Width(), rectClient.Height(),
&memDC, 0, 0, SRCCOPY) ;
}
memDC.SelectObject(oldBitmap) ;
//删除内存位图GDI对象
memBitmap.DeleteObject();
//删除内存绘图环境
memDC.DeleteDC();
// Do not call CWnd::OnPaint() for painting messages
}
BOOL CMyButton::CreateBTN(LPCTSTR lpszWindowName, DWORD dwStyle,
const
RECT
&
rect,
CWnd
*
pParentWnd, UINT nID,LPDealWithBTN lpDealWithBTN
/**/
/* = NULL*/
)
...
{
// TODO: Add your specialized code here and/or call the base class
BOOL bresult ;
//注册窗体类
static CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
//创建窗体类
bresult = CWnd::CreateEx(NULL, className, NULL, dwStyle,
rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
pParentWnd->GetSafeHwnd(), (HMENU)nID) ;
//更新窗体显示
if (bresult != 0)
...{
m_strTitleInfo = lpszWindowName;
m_lpDealWithBTN = lpDealWithBTN;
InitMyButton();
DrawBackForNor();
}
return TRUE;
// return CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
}
void
CMyButton::InitMyButton()
...
{
CClientDC dc(this);
// CBrush brushBack;
CRect rect;
GetClientRect(rect);
// brushBack.CreateSolidBrush(m_clNorBack) ;
//创建画线设备环境以及创建相应缓冲区
if (m_dcBack.GetSafeHdc() == NULL)
...{
m_dcBack.CreateCompatibleDC(&dc) ;
m_bmpBack.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height()) ;
m_dcBack.SelectObject(&m_bmpBack) ;
}
m_dcBack.SetBkColor (m_clNorBack) ;
m_dcBack.FillSolidRect(rect,m_clNorBack);
}
void
CMyButton::DrawBackForFoc()
...
{
CBrush bRushBack(m_clFocBack);
CBrush *OldBrush = m_dcBack.SelectObject(&bRushBack);
CPen penBlack;
penBlack.CreatePen(PS_SOLID,1,RGB(0,0,0));
CPen *penOld = m_dcBack.SelectObject(&penBlack);
CRect rect;
GetClientRect(&rect);
m_dcBack.FillSolidRect(rect,RGB(192,192,192));
// rect.DeflateRect(5,5);
m_dcBack.RoundRect(rect,CPoint(4,4));
m_dcBack.SelectObject(OldBrush);
m_dcBack.SelectObject(penOld);
bRushBack.DeleteObject();
penBlack.DeleteObject();
int nLenthBuf = WideCharToMultiByte(CP_ACP,0,m_strTitleInfo,-1,NULL,0,NULL,NULL) - 1;
LONG lLeftX = rect.left + (rect.Width() - nLenthBuf * 6) / 2;
LONG lLeftY = rect.top + (rect.Height() - 14) / 2;
m_dcBack.SetBkColor(m_clFocBack);
m_dcBack.SetTextColor(m_clFocText);
m_dcBack.ExtTextOut(lLeftX,lLeftY,ETO_OPAQUE,NULL,m_strTitleInfo,NULL);
Invalidate(FALSE);
m_bLastState = true;
}
void
CMyButton::DrawBackForMid()
...
{
CBrush bRushBack(RGB(230,230,207));
CBrush *OldBrush = m_dcBack.SelectObject(&bRushBack);
CPen penBlack;
penBlack.CreatePen(PS_SOLID,1,RGB(128,128,128));
CPen *penOld = m_dcBack.SelectObject(&penBlack);
CRect rect;
GetClientRect(&rect);
m_dcBack.FillSolidRect(rect,RGB(192,192,192));
// rect.DeflateRect(5,5);
m_dcBack.RoundRect(rect,CPoint(4,4));
m_dcBack.SelectObject(OldBrush);
m_dcBack.SelectObject(penOld);
bRushBack.DeleteObject();
penBlack.DeleteObject();
int nLenthBuf = WideCharToMultiByte(CP_ACP,0,m_strTitleInfo,-1,NULL,0,NULL,NULL) - 1;
LONG lLeftX = rect.left + (rect.Width() - nLenthBuf * 6) / 2;
LONG lLeftY = rect.top + (rect.Height() - 14) / 2;
m_dcBack.SetBkColor(RGB(230,230,207));
m_dcBack.SetTextColor(RGB(128,128,128));
m_dcBack.ExtTextOut(lLeftX,lLeftY,ETO_OPAQUE,NULL,m_strTitleInfo,NULL);
Invalidate(FALSE);
}
//
normal
void
CMyButton::DrawBackForNor()
...
{
CRect rect;
GetClientRect(&rect);
CBrush bRushBack(m_clNorBack);
CBrush *OldBrush = m_dcBack.SelectObject(&bRushBack);
CPen penBlack;
penBlack.CreatePen(PS_SOLID,2,RGB(100,100,192));
CPen *penOld = m_dcBack.SelectObject(&penBlack);
m_dcBack.FillSolidRect(rect,RGB(192,192,192));
//rect.DeflateRect(5,5);
// m_dcBack.RoundRect(rect,CPoint(4,4));
m_dcBack.FillSolidRect(rect,RGB(100,100,192));
m_dcBack.SelectObject(OldBrush);
m_dcBack.SelectObject(penOld);
bRushBack.DeleteObject();
penBlack.DeleteObject();
int nLenthBuf = WideCharToMultiByte(CP_ACP,0,m_strTitleInfo,-1,NULL,0,NULL,NULL) - 1;
LONG lLeftX = rect.left + (rect.Width() - nLenthBuf * 6) / 2;
LONG lLeftY = rect.top + (rect.Height() - 14) / 2;
m_dcBack.SetBkColor(m_clNorBack);
m_dcBack.SetTextColor(m_clNorText);
m_dcBack.ExtTextOut(lLeftX,lLeftY,ETO_OPAQUE,NULL,m_strTitleInfo,NULL);
Invalidate(FALSE);
m_bLastState = false;
}
void
CMyButton::OnSetFocus(CWnd
*
pOldWnd)
...
{
CWnd::OnSetFocus(pOldWnd);
// TODO: Add your message handler code here
if (!m_bLastState)
...{
DrawBackForFoc();
}
}
void
CMyButton::OnKillFocus(CWnd
*
pNewWnd)
...
{
CWnd::OnKillFocus(pNewWnd);
// TODO: Add your message handler code here
if (m_bLastState)
...{
DrawBackForNor();
}
}
void
CMyButton::OnLButtonDown(UINT nFlags, CPoint point)
...
{
// TODO: Add your message handler code here and/or call default
if (m_lpDealWithBTN != NULL)
...{
m_lpDealWithBTN();
}
SetFocus();
CWnd::OnLButtonDown(nFlags, point);
}
void
CMyButton::SetWindowText(LPCTSTR lpszString)
...
{
m_strTitleInfo = lpszString;
if (m_bLastState)
...{
DrawBackForFoc();
}
else
...{
DrawBackForNor();
}
}
BOOL CMyButton::EnableWindow(BOOL bEnable
/**/
/* = TRUE */
)
...
{
if (bEnable)
...{
DrawBackForNor();
}
else
...{
DrawBackForMid();
}
return CWnd::EnableWindow(bEnable);
}
注意:所有代码中ExtTextOut 完全可以用DrawText来代替,更简单一些。
为了实现主窗体跟按钮之间的通信,例如当按钮按下时,按钮要通知主窗体,我在这里添加函数指针来实现,当然也可以通过消息的方式来实现。声明函数指针:typedef void (*LPDealWithBTN)();
LPDealWithBTN m_lpDealWithBTN;//函数指针
把所有代码添加完毕就可以实现效果了。在主程序就可以添加相关的函数,如果要使用函数指针来实现通知的话,需先建立一个static函数。例如static void OnDealWithBTN01();
test:
CMyButton m_myBTN01;
m_myBTN01.CreateBTN(L"TestBTN",WS_CHILD|WS_VISIBLE,CRect(260,110,335,130),this,0,OnDealWithBTN01);
void CTestBtnDlg::OnDealWithBTN01()
{
printf("BTN01 pressed/r/n");
}
当然也可以实现贴图,这是完全可以的。如果要贴图的话,又要实现透明效果图片的话,那么参考一下代码:
//
画透明图片
void
CMyButton::DrawTransParent(CBitmap
&
bmp,
int
const
x,
int
const
y,
int
const
cx,
int
const
cy,
int
const
srcx,
int
const
srcy,CDC
*
pDC,COLORREF TransparentColor)
...
{
//定义源、掩码、透明绘制环境
CDC SrcDC,MaskDC,TransDC;
//定义掩码、透明位图
CBitmap MaskBmp,TransBmp;
BITMAP bm;
//创建源、掩码、透明绘制环境
SrcDC.CreateCompatibleDC(pDC);
MaskDC.CreateCompatibleDC(pDC);
TransDC.CreateCompatibleDC(pDC);
//得到位图结构
bmp.GetBitmap(&bm);
//创建透明位图
TransBmp.CreateCompatibleBitmap(pDC,cx,cy);
//创建掩码位图
MaskBmp.CreateBitmap(cx,cy,1,1,NULL);
//将透明位图对象选入到透明绘图环境
CBitmap *pTransBmp = TransDC.SelectObject(&TransBmp);
//将目标位图绘制到透明位图中
TransDC.BitBlt(x,y,cx,cy,pDC,srcx,srcy,SRCCOPY);
//将实际位图对象选入源绘图环境
CBitmap *pSrcBmp = SrcDC.SelectObject(&bmp);
//设置背景为透明模式
SrcDC.SetBkMode(TRANSPARENT);
//设置背景色
if (TransparentColor)
SrcDC.SetBkColor(TransparentColor);
else
...{
COLORREF col = SrcDC.GetPixel(0,bm.bmHeight-1);
SrcDC.SetBkColor(col);
}
CBitmap *pMaskBmp = MaskDC.SelectObject(&MaskBmp);
//将源绘图环境绘制到掩码绘图环境中
MaskDC.BitBlt(x,y,cx,cy,&SrcDC,0,0,SRCCOPY);
//将源位图和透明位图进行异或操作融合
TransDC.BitBlt(x,y,cx,cy,&SrcDC,0,0,SRCINVERT);
//将透明位图和掩码位图进行与操作
TransDC.BitBlt(x,y,cx,cy,&MaskDC,0,0,SRCAND);
//再将源位图和透明位图进行异或操作
TransDC.BitBlt(x,y,cx,cy,&SrcDC,0,0,SRCINVERT);
//此时将透明位图绘制到目标设备上
BOOL bRet = pDC->BitBlt(x,y,cx,cy,&TransDC,srcx,srcy,SRCCOPY);
//绘制绘图对象,并释放相应绘图环境和位图对象
SrcDC.SelectObject(pSrcBmp);
SrcDC.DeleteDC();
TransDC.SelectObject(pTransBmp);
TransBmp.DeleteObject();
TransDC.DeleteDC();
MaskDC.SelectObject(pMaskBmp);
MaskDC.DeleteDC();
MaskBmp.DeleteObject();
}
调用时:
CBitmap bmp;
bmp.LoadBitmap(IDB_BITMAP_MID);
BITMAP bm;
bmp.GetBitmap(&bm);
DrawTransParent(bmp,0,0,bm.bmWidth,bm.bmHeight,0,0,&m_dcBack,NULL);
bmp.DeleteObject();
注:原文出处http://blog.csdn.net/KevinCEC/archive/2008/01/22/2059564.aspx