━━━━━━━━━━━━━━━━━━━━━━━━
突然心血来潮,想写一个自绘按钮的类。以前做这事会觉得很辛苦,但现在随着自己知识和经验的增长,一切都变得简单很多了。刚刚做好,现与诸位分享。若有不正确之处,还望各位海涵并给于斧正。阿弥陀佛!^-^
━━━━━━━━━━━━━━━━━━━━━━━━
一、如何使用:
1.把MyButton.cpp 和 MyButton.h 添加到你的工程里。
2.包含头文件 #include "MyButton.h"
3.在OnInitDialog()中添加以代码。(一切搞掂,是不是很方便呢)
BOOL CDemoDlg::OnInitDialog()
{
//子类化单个按钮。
//button close 时在 PostNcDestroy() 会调用delete this清除对象
CMyButton*p1 =new CMyButton(m_hWnd,IDC_BUTTON1);
CMyButton*p2 =new CMyButton(m_hWnd,IDC_BUTTON2);
return TRUE;
}
4.需要子类化所有button时,按如下调用:
BOOL CDemoDlg::OnInitDialog()
{
//子类化所有按钮
CMyButton::SubclassAllButton(m_hWnd);
return TRUE;
}
━━━━━━━━━━━━━━━━━━━━━━━━
二、几个重点:
1.要设置button为自绘。如下代码“ModifyStyle(0, BS_OWNERDRAW);”
2、要手动添加鼠标离开窗口的消息(若不懂,请看捕获鼠标离开窗口的消息)
http://hi.baidu.com/qiujiejia/blog/item/f22d012488c7880f4c088def.html
3.成员函数DrawGradientV是画一个颜色渐变的矩形,你可以把它直接移植出来应用在你的工程里
4.DrawMyItem函数分成三个步骤。
(1)画渐变背景
(2)画空的矩形框作为按钮的边框
(3)画文字(如果鼠标按下,则偏移文字)
━━━━━━━━━━━━━━━━━━━━━━━━
三、把自绘按钮封装成一个dll
你只需要把ButtonSkin.dll放在工程目录下,并且在对话框初始化时添加以下代码即可使所有button都换上皮肤,很方便。
//加载按钮皮肤
HINSTANCE hInst1;
if (hInst1=LoadLibrary("ButtonSkin.dll"))
{
void (*SubclassButton)(HWND)=(void(*)(HWND))GetProcAddress(hInst1,"SubclassButton");
if(SubclassButton) SubclassButton(m_hWnd);
}
ButtonSkin.dll 下载:ButtonSkin.dll
详细代码请参考源程序:ButtonSkin source code
━━━━━━━━━━━━━━━━━━━━━━━━
四、部分重要代码
CMyButton::CMyButton(HWND ParentWnd,UINT nID)
{
SubclassWindow(::GetDlgItem(ParentWnd,nID)); //子类化
}
void CMyButton::PreSubclassWindow()
{
m_IsInWindow=false;
ModifyStyle(0, BS_OWNERDRAW); //修改为自绘button
::GetClientRect(m_hWnd,&rect); //获取button的矩形区域
CButton::PreSubclassWindow();
}
/****************************************************************************
//子类化所有按钮
****************************************************************************/
void CMyButton::SubclassAllButton(HWND ParentWnd)
{
HWND hChild = ::GetWindow(ParentWnd, GW_CHILD);
while(hChild != NULL)
{
TCHAR lpClassName[MAX_PATH];
::GetClassName(hChild,lpClassName,MAX_PATH); //获得窗口类名
if (_tcscmp(lpClassName,_T("Button"))==0)
{
// 判断按钮类型, 确保不是单选或复选按钮
DWORD dwStyle = GetWindowLong(hChild, GWL_STYLE);
dwStyle = dwStyle & 0x0000000F;
if(dwStyle == BS_PUSHBUTTON || dwStyle == BS_DEFPUSHBUTTON)
{
//button close 时在 PostNcDestroy() 会调用delete this清除对象
CMyButton* p=new CMyButton();
p->SubclassWindow(hChild); //子类化
}
}
hChild=::GetWindow(hChild, GW_HWNDNEXT);
}
}
/****************************************************************************
在鼠标第一次进入窗口时,绘制按钮的over效果
****************************************************************************/
void CMyButton::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_IsInWindow)
{
m_IsInWindow = TRUE;
TRACKMOUSEEVENT trackmouseevent;
trackmouseevent.cbSize = sizeof(trackmouseevent);
trackmouseevent.dwFlags = TME_LEAVE;
trackmouseevent.hwndTrack = GetSafeHwnd();
trackmouseevent.dwHoverTime = HOVER_DEFAULT;
_TrackMouseEvent(&trackmouseevent);
//draw move over effect
DrawMyItem(RGB(0,225,225),RGB(175,238,238));
}
CButton::OnMouseMove(nFlags, point);
}
/****************************************************************************
鼠标离开按钮,置m_IsInWindow为false,并刷新按钮
****************************************************************************/
LRESULT CMyButton::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
m_IsInWindow=false;
InvalidateRect(NULL);
return 0;
}
/****************************************************************************
核心代码。
COLORREF co1, COLORREF co2 组合成渐变色
****************************************************************************/
void CMyButton::DrawMyItem(COLORREF co1, COLORREF co2)
{
HDC hdc=::GetDC(m_hWnd);
//draw gradient effect
DrawGradientV(hdc,co1,co2,rect);
//draw a null rectangle as the button frame
HBRUSH OldBrush=(HBRUSH)::SelectObject(hdc,(HBRUSH)GetStockObject(NULL_BRUSH));
::Rectangle(hdc,0,0,rect.right-rect.left,rect.bottom-rect.top);
::SelectObject (hdc,OldBrush) ;
//draw button text
TCHAR WindowText[100];
::GetWindowText(m_hWnd, WindowText, MAX_PATH);
//设置文字透明
int nMode=::SetBkMode(hdc,TRANSPARENT);
//鼠标按下时字体偏移
if (GetKeyState(VK_LBUTTON)<0)
{
CRect TempRect=rect;
TempRect.OffsetRect(1,1);
::DrawText (hdc,WindowText, -1,&TempRect,DT_SINGLELINE | DT_CENTER | DT_VCENTER |DT_VCENTER|DT_END_ELLIPSIS ) ;
}
else
::DrawText (hdc,WindowText, -1,&rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER |DT_VCENTER|DT_END_ELLIPSIS ) ;
::SetBkMode(hdc,nMode);
::ReleaseDC(m_hWnd,hdc);
}
/****************************************************************************
收到消息要求我们来绘制button
****************************************************************************/
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (lpDrawItemStruct->itemState & ODS_FOCUS )
{
if (lpDrawItemStruct->itemState & ODS_SELECTED ) //鼠标按下并且在窗口外
DrawMyItem(RGB(0,255,0),RGB(0,255,200));
else
DrawMyItem(RGB(49,68,212),RGB(147,255,255)); //获得焦点的效果
}
else
DrawMyItem(RGB(253,181,81),RGB(253,255,206)); //没有获得焦点的效果
}
/****************************************************************************
绘制垂直颜色渐变区域
DrawGradientV( HDC hdc //绘图刷子
COLORREF co1 //顶端颜色
COLORREF co2 //低端颜色
RECT& DrawRect) //颜色渐变区域
****************************************************************************/
//设置渐变参数,GRADLEVEL越小,颜色渐变越细腻,过度效果越好,但速度比较慢
#define GRADLEVEL 1
void CMyButton::DrawGradientV( HDC hdc, COLORREF co1, COLORREF co2, RECT& DrawRect )
{
int r = GetRValue( co1 );
int g = GetGValue( co1 );
int b = GetBValue( co1 );
int r2 = GetRValue( co2 );
int g2 = GetGValue( co2 );
int b2 = GetBValue( co2 );
//计算宽,高
int DrawRectWidth=DrawRect.right-DrawRect.left;
int DrawRectHeight=DrawRect.bottom-DrawRect.top;
if ( DrawRectWidth<=0)
return;
//初始化rect
RECT rect={0,0,DrawRectWidth,GRADLEVEL};
//准备GDI
HDC hMemDC=CreateCompatibleDC(hdc); //创建内存DC
HBITMAP hBitmap=::CreateCompatibleBitmap(hdc,DrawRectWidth,DrawRectHeight);//创建位图
::SelectObject(hMemDC,hBitmap); //把位图选进内存DC
HBRUSH hbr;
for(int i = DrawRectHeight; i > 0; i -= GRADLEVEL )
{
//创建刷子
hbr = CreateSolidBrush( RGB( r, g, b ) );
FillRect( hMemDC, &rect, hbr );
DeleteObject( hbr );
//改变小正方体的位置
rect.top += GRADLEVEL;
rect.bottom += GRADLEVEL;
//判断小正方体是否超界
if( rect.bottom > DrawRect.bottom )
rect.bottom = DrawRect.bottom;
//改变颜色
r += ( r2 - r + i / 2 ) / i * GRADLEVEL;
g += ( g2 - g + i / 2 ) / i * GRADLEVEL;
b += ( b2 - b + i / 2 ) / i * GRADLEVEL;
}
//内存DC映射到屏幕DC
BitBlt(hdc,DrawRect.left,DrawRect.top,DrawRectWidth,DrawRectHeight,hMemDC,0,0,SRCCOPY);
//删除
::DeleteDC(hMemDC) ;
::DeleteObject(hBitmap);
}
void CMyButton::PostNcDestroy()
{
delete this;
CButton::PostNcDestroy();
}
━━━━━━━━━━━━━━━━━━━━━━━━