自绘按钮的实现

//========================================================================
//TITLE:
// 自绘按钮的实现
//AUTHOR:
// norains
//DATE:
// Saturday 12-January-2008
//Environment:
// VS2005 + SDK-WINCE5.0-MIPSII
// EVC + SDK-WINCE5.0-MIPSII
//========================================================================

自绘按钮的实现并不难,只不过如果是在非MFC类库中实现,却是显得有点麻烦,或是说,更需要一点小技巧.

文章打算在CMainWnd窗口类中实现一个自绘按钮,为了方便讲解以及突出重点,CMainWnd直接派生于CWndBase类.关于CWndBase类的信息,可以在此找到:http://blog.csdn.net/norains/archive/2008/01/12/2040109.aspx

首先我们先要创建一个按钮,我将它命名为m_hBnExit:

m_hBnExit = CreateWindowEx(WS_EX_TOPMOST,
TEXT(
" BUTTON " ),
TEXT(
"" ),
BS_PUSHBUTTON
| WS_VISIBLE | WS_CHILD | BS_OWNERDRAW,
POS_EXIT.left,
POS_EXIT.top,
POS_EXIT.right
- POS_EXIT.left,
POS_EXIT.bottom
- POS_EXIT.top,
m_hWnd,
(HMENU)IDC_BTN_EXIT,
m_hInst,
NULL);

这里有点需要注意,因为我们需要重新绘制按钮,所以BS_OWNERDRAW风格一定需要设置.一旦设置该风格,CMainWnd窗口的消息处理函数就能接收到按钮的WM_DRAWITEM消息:
LRESULTCMainWnd::WndProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
...

switch (wMsg)
{
case WM_DRAWITEM:
if (((LPDRAWITEMSTRUCT)lParam) -> CtlID == IDC_BTN_EXIT)
{
// TODOsomething;
}

...

}

...
}

接下来需要做的就很简单了,我们只需要在接收到WM_DRAWITEM消息时,判断是否为退出按键的ID号(在本文例子中为IDC_BTN_EXIT),如果是的话就重新绘制.为此,我们定义一个OnDrawItemBtnExit()函数,用来绘制该按钮:
void CMainWnd::OnDrawItemBtnExit(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
LPDRAWITEMSTRUCTlpdis
= (LPDRAWITEMSTRUCT)lParam;

// 读取按钮的图片
HANDLEhBmp = LoadImage(m_hInst,MAKEINTRESOURCE(IDB_EXIT),IMAGE_BITMAP, 0 , 0 , 0 );
if (hBmp == NULL)
{
return ;
}

// Drawthebutton
HDChdcBmp = CreateCompatibleDC(lpdis -> hDC);
HGDIOBJhOldSel
= SelectObject(hdcBmp,hBmp);

if ((lpdis -> itemState & ODS_SELECTED) && ! (lpdis -> itemState & ODS_DISABLED))
{
// 按下状态
TransparentBlt(lpdis -> hDC,
lpdis
-> rcItem.left,
lpdis
-> rcItem.top,
lpdis
-> rcItem.right - lpdis -> rcItem.left,
lpdis
-> rcItem.bottom - lpdis -> rcItem.top,
hdcBmp,
IMG_EXIT_PUSH.left,
IMG_EXIT_PUSH.top,
IMG_EXIT_PUSH.right
- IMG_EXIT_PUSH.left,
IMG_EXIT_PUSH.bottom
- IMG_EXIT_PUSH.top,
DEFAULT_TRANSPARENT_COLOR);
}
else if (lpdis -> itemState & ODS_DISABLED)
{
// 无效状态
TransparentBlt(lpdis -> hDC,
lpdis
-> rcItem.left,
lpdis
-> rcItem.top,
lpdis
-> rcItem.right - lpdis -> rcItem.left,
lpdis
-> rcItem.bottom - lpdis -> rcItem.top,
hdcBmp,
IMG_EXIT_DISABLE.left,
IMG_EXIT_DISABLE.top,
IMG_EXIT_DISABLE.right
- IMG_EXIT_DISABLE.left,
IMG_EXIT_DISABLE.bottom
- IMG_EXIT_DISABLE.top,
DEFAULT_TRANSPARENT_COLOR);
}
else
{
// 正常状态
TransparentBlt(lpdis -> hDC,
lpdis
-> rcItem.left,
lpdis
-> rcItem.top,
lpdis
-> rcItem.right - lpdis -> rcItem.left,
lpdis
-> rcItem.bottom - lpdis -> rcItem.top,
hdcBmp,
IMG_EXIT_ENABLE.left,
IMG_EXIT_ENABLE.top,
IMG_EXIT_ENABLE.right
- IMG_EXIT_ENABLE.left,
IMG_EXIT_ENABLE.bottom
- IMG_EXIT_ENABLE.top,
DEFAULT_TRANSPARENT_COLOR);
}

if (hdcBmp != NULL && hOldSel != NULL)
{
SelectObject(hdcBmp,hOldSel);
DeleteDC(hdcBmp);

hdcBmp
= NULL;
hOldSel
= NULL;
}

if (hBmp != NULL)
{
DeleteObject(hBmp);
hBmp
= NULL;
}

}

不过这么一来会有个很明显的问题,如图1:


我们发现在按钮周围有一圈非常不雅的灰色地带,而这灰色区域是窗口创建的时候自绘的.有经验的朋友都知道,如果是自己创建的窗口,要让这灰色区域消失是非常简单的事情,只需要在调用RegisterClass函数时选择一个无颜色的画刷即可.但由于所有的Windows Control的注册函数都是封装的,我们无法对其参数进行变更,所以唯一的方法只能是另寻窍门.

所谓的去掉灰色地带,无非就是把主窗口那处被掩盖的区域重新绘制出来.有这个思路一切就好办了,我们只需要在主窗口的WM_PAINT响应函数中存储主窗口的DC数据,然后在按钮重绘的时候再把它显示出来即可:
void CMainWnd::OnPaint(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
...

// 存储主窗口的所有DC数据到m_hdcBufBk中
// CreatethememoryDCforstoringthebackground.
m_hBmpBufBk = CreateCompatibleBitmap(hdc,rcWnd.right - rcWnd.left,rcWnd.bottom - rcWnd.top);
m_hdcBufBk
= CreateCompatibleDC(hdc);
m_hOldSelBufBk
= SelectObject(m_hdcBufBk,m_hBmpBufBk);


// Drawthebackground
HPENhPen = CreatePen(PS_SOLID, 1 ,RGB( 0 , 255 , 0 ));
HPENhOldPen
= NULL;
hOldPen
= (HPEN)SelectObject(m_hdcBufBk,hPen);
// therectcolor
HBRUSHhBrush = CreateSolidBrush(RGB( 0 , 255 , 0 ));
HGDIOBJhOldBrush
= SelectObject(m_hdcBufBk,hBrush);
// Draw
Rectangle(m_hdcBufBk, 0 , 0 ,rcWnd.right - rcWnd.left + 1 ,rcWnd.bottom - rcWnd.top + 1 );
// Realsetheresource
SelectObject(m_hdcBufBk,hOldBrush);
DeleteObject(hBrush);
SelectObject(m_hdcBufBk,hOldPen);
DeleteObject(hPen);

...

}

然后在按钮的自绘函数中将其绘制出来:
void CMainWnd::OnDrawItemBtnExit(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
...

// Drawthebackground
BitBlt(lpdis -> hDC,
lpdis
-> rcItem.left,
lpdis
-> rcItem.top,
lpdis
-> rcItem.right - lpdis -> rcItem.left,
lpdis
-> rcItem.bottom - lpdis -> rcItem.top,
m_hdcBufBk,
POS_EXIT.left,
POS_EXIT.top,
SRCCOPY);

...
}

添加背景绘制代码之后显示的效果如图2所示:


如果你的目的仅仅是用图片替代原来那种死板的按钮样式的话,那么到这步已经是大功告成,可以举杯欢庆了.但如果你还需要更多的需求,比如显示出通过SetWindowText设置的字符串,那么你还必须要做更多的工作.

因为我们无法直接通过GetWindowText获取设置文本的真正长度,所以我们必须在按钮接收WM_SETTEXT消息时获取窗口文本.也许有人说,我设置一个很大的缓冲区不就行了么?可是,你需要设置多大?最好不要抱这种想法,因为我们永远不知道用户的真正意思!

除了文档中明白指出的按钮消息,其它的消息我们都无法在MainProc函数中截获,比如按钮的WM_PAINT,WM_ERASEBKGND等.所以,这里我们需要在创建按钮控件的时候用SetWindowLong函数来转移按钮消息(关于SetWindowLong与消息转移可以参加我这篇文章:http://blog.csdn.net/norains/archive/2008/01/03/2023986.aspx).
// Storethepointerinthewindow
SetWindowLong(m_hBnExit,GWL_USERDATA,(DWORD) this );
// Setthenewwindowprocedure
m_PreProcBnExit = (WNDPROC)SetWindowLong(m_hBnExit,GWL_WNDPROC,(DWORD)CtrlProc);

我们需要在转移的消息处理函数中截获WM_SETTEXT消息,并分配合适的内存空间存储设置的字符串:
LRESULTCMainWnd::CtrlProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
CMainWnd
* pObject = (CMainWnd * )GetWindowLong(hWnd,GWL_USERDATA);
if (pObject != NULL)
{
if (hWnd == pObject -> m_hBnExit)
{
switch (wMsg)
{
case WM_SETTEXT:
{
if (pObject -> m_pszBnExit != NULL)
{
delete[]pObject
-> m_pszBnExit;
pObject
-> m_pszBnExit = NULL;
}
pObject
-> m_pszBnExit = new TCHAR[_tcslen(reinterpret_cast < LPCTSTR > (lParam)) + 1 ];
_tcscpy(pObject
-> m_pszBnExit,reinterpret_cast < LPCTSTR > (lParam));
break ;
}
}
return CallWindowProc(pObject -> m_PreProcBnExit,hWnd,wMsg,wParam,lParam);
}
}

return DefWindowProc(hWnd,wMsg,wParam,lParam);

}

既然我们已经获得了字符串,要绘制出来就是轻而易举的事情了:
void CMainWnd::OnDrawItemBtnExit(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
...

// Drawthetext
SetBkMode(lpdis -> hDC,TRANSPARENT);
DrawText(lpdis
-> hDC,m_pszBnExit, - 1 , & lpdis -> rcItem,DT_CENTER | DT_VCENTER);

...
}

该工程可以在此下载:http://download.csdn.net/source/333479

CMainWnd完整的代码如下:

#pragma once

#include
" Wndbase.h "

class CMainWnd:
public CWndBase
{
public :

virtual BOOLCreate(HINSTANCEhInst,HWNDhWndParent, const TCHAR * pcszWndClass, const TCHAR * pcszWndName);
CMainWnd(
void );
~ CMainWnd( void );

protected :
virtual LRESULTWndProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam);
static LRESULTCtrlProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam);
void OnClickedBtnExit(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam);
void OnDrawItemBtnExit(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam);
void OnPaint(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam);



private :
HDCm_hdcBufBk;
// Thedcbufferisjustforstoringthebackgrounddata.
HBITMAPm_hBmpBufBk;
HGDIOBJm_hOldSelBufBk;
HWNDm_hBnExit;
TCHAR
* m_pszBnExit;
WNDPROCm_PreProcBnExit;
};




#include
" stdafx.h "
#include
" resource.h "
#include
" MainWnd.h "


// *****************************************************************************************************
// Thedefaulttransparentcolorfortheimage
#define DEFAULT_TRANSPARENT_COLORRGB(255,0,255)


// *****************************************************************************************************
// Thestructdata

// Theimagedisplayedinformation
#ifndef_DISPIMAGEINFO
#define _DISPIMAGEINFO
typedef
struct
{
LONGimgID;
LONGleft;
LONGtop;
LONGright;
LONGbottom;
}DISPIMAGEINFO,
* PDISPIMAGEINFO;
#endif // _DISPIMAGEINFO


// ------------------------------------------------------------------------------
// Theresourceinformation

const DISPIMAGEINFOIMG_EXIT_ENABLE = {IDB_EXIT, 1 , 1 , 46 , 46 };
const DISPIMAGEINFOIMG_EXIT_DISABLE = {IDB_EXIT, 93 , 1 , 138 , 46 };
const DISPIMAGEINFOIMG_EXIT_PUSH = {IDB_EXIT, 47 , 1 , 92 , 46 };

// *****************************************************************************************************

// -------------------------------------------------------------------
// Macro
#define IDC_BTN_EXIT103
// -------------------------------------------------------------------
// constvalue
const RECTPOS_EXIT = { 0 , 0 , 45 , 45 };
// -------------------------------------------------------------------

CMainWnd::CMainWnd(
void ):
m_hBnExit(NULL),
m_hdcBufBk(NULL),
m_hBmpBufBk(NULL),
m_hOldSelBufBk(NULL),
m_PreProcBnExit(NULL),
m_pszBnExit(NULL)
{
}

CMainWnd::
~ CMainWnd( void )
{
if (m_hdcBufBk != NULL && m_hOldSelBufBk != NULL)
{
SelectObject(m_hdcBufBk,m_hOldSelBufBk);
}

if (m_hBmpBufBk != NULL)
{
DeleteObject(m_hBmpBufBk);

m_hBmpBufBk
= NULL;
}

if (m_hdcBufBk != NULL)
{
DeleteDC(m_hdcBufBk);

m_hdcBufBk
= NULL;
}

if (m_pszBnExit != NULL)
{
delete[]m_pszBnExit;

m_pszBnExit
= NULL;
}

}


// ----------------------------------------------------------------------
// Description:
// Createthewindow.It'soverridefunction
//
// ----------------------------------------------------------------------
BOOLCMainWnd::Create(HINSTANCEhInst,HWNDhWndParent, const TCHAR * pcszWndClass, const TCHAR * pcszWndName)
{

CWndBase::Create(hInst,hWndParent,pcszWndClass,pcszWndName);


// Thebuttonforexiting
m_hBnExit = CreateWindowEx(WS_EX_TOPMOST,
TEXT(
" BUTTON " ),
TEXT(
"" ),
BS_PUSHBUTTON
| BS_CENTER | WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_OWNERDRAW,
POS_EXIT.left,
POS_EXIT.top,
POS_EXIT.right
- POS_EXIT.left,
POS_EXIT.bottom
- POS_EXIT.top,
m_hWnd,
(HMENU)IDC_BTN_EXIT,
m_hInst,
NULL);


// Storethepointerinthewindow
SetWindowLong(m_hBnExit,GWL_USERDATA,(DWORD) this );
// Setthenewwindowprocedure
m_PreProcBnExit = (WNDPROC)SetWindowLong(m_hBnExit,GWL_WNDPROC,(DWORD)CtrlProc);


SetWindowText(m_hBnExit,TEXT(
" X " ));

return TRUE;
}

// ----------------------------------------------------------------------
// Description:
// Windowprocess.It'soverridefunction
//
// ----------------------------------------------------------------------
LRESULTCMainWnd::WndProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
switch (wMsg)
{
case WM_DRAWITEM:
if (((LPDRAWITEMSTRUCT)lParam) -> CtlID == IDC_BTN_EXIT)
{
OnDrawItemBtnExit(hWnd,wMsg,wParam,lParam);
return 0 ;
}
case WM_COMMAND:
{
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_BTN_EXIT)
{
OnClickedBtnExit(hWnd,wMsg,wParam,lParam);
}
}
case WM_PAINT:
{
OnPaint(hWnd,wMsg,wParam,lParam);
return 0 ;
}
}

return DefWindowProc(hWnd,wMsg,wParam,lParam);
}

// ----------------------------------------------------------------------
// Description:
// Windowscontrolprocess
//
// ----------------------------------------------------------------------
LRESULTCMainWnd::CtrlProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
CMainWnd
* pObject = (CMainWnd * )GetWindowLong(hWnd,GWL_USERDATA);
if (pObject != NULL)
{
if (hWnd == pObject -> m_hBnExit)
{
switch (wMsg)
{
case WM_SETTEXT:
{
if (pObject -> m_pszBnExit != NULL)
{
delete[]pObject
-> m_pszBnExit;
pObject
-> m_pszBnExit = NULL;
}
pObject
-> m_pszBnExit = new TCHAR[_tcslen(reinterpret_cast < LPCTSTR > (lParam)) + 1 ];
_tcscpy(pObject
-> m_pszBnExit,reinterpret_cast < LPCTSTR > (lParam));
break ;
}
}
return CallWindowProc(pObject -> m_PreProcBnExit,hWnd,wMsg,wParam,lParam);
}
}

return DefWindowProc(hWnd,wMsg,wParam,lParam);

}
// -----------------------------------------------------------------------------------------
// Description:
// OnthemessageWM_DRAWITEManditisforthebuttonexit
//
// ----------------------------------------------------------------------------------------------
void CMainWnd::OnDrawItemBtnExit(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
LPDRAWITEMSTRUCTlpdis
= (LPDRAWITEMSTRUCT)lParam;

HANDLEhBmp
= LoadImage(m_hInst,MAKEINTRESOURCE(IDB_EXIT),IMAGE_BITMAP, 0 , 0 , 0 );
if (hBmp == NULL)
{
return ;
}


// Drawthebackground
BitBlt(lpdis -> hDC,
lpdis
-> rcItem.left,
lpdis
-> rcItem.top,
lpdis
-> rcItem.right - lpdis -> rcItem.left,
lpdis
-> rcItem.bottom - lpdis -> rcItem.top,
m_hdcBufBk,
POS_EXIT.left,
POS_EXIT.top,
SRCCOPY);


// Drawthebutton
HDChdcBmp = CreateCompatibleDC(lpdis -> hDC);
HGDIOBJhOldSel
= SelectObject(hdcBmp,hBmp);

if ((lpdis -> itemState & ODS_SELECTED) && ! (lpdis -> itemState & ODS_DISABLED))
{
TransparentBlt(lpdis
-> hDC,
lpdis
-> rcItem.left,
lpdis
-> rcItem.top,
lpdis
-> rcItem.right - lpdis -> rcItem.left,
lpdis
-> rcItem.bottom - lpdis -> rcItem.top,
hdcBmp,
IMG_EXIT_PUSH.left,
IMG_EXIT_PUSH.top,
IMG_EXIT_PUSH.right
- IMG_EXIT_PUSH.left,
IMG_EXIT_PUSH.bottom
- IMG_EXIT_PUSH.top,
DEFAULT_TRANSPARENT_COLOR);
}
else if (lpdis -> itemState & ODS_DISABLED)
{
TransparentBlt(lpdis
-> hDC,
lpdis
-> rcItem.left,
lpdis
-> rcItem.top,
lpdis
-> rcItem.right - lpdis -> rcItem.left,
lpdis
-> rcItem.bottom - lpdis -> rcItem.top,
hdcBmp,
IMG_EXIT_DISABLE.left,
IMG_EXIT_DISABLE.top,
IMG_EXIT_DISABLE.right
- IMG_EXIT_DISABLE.left,
IMG_EXIT_DISABLE.bottom
- IMG_EXIT_DISABLE.top,
DEFAULT_TRANSPARENT_COLOR);
}
else
{
TransparentBlt(lpdis
-> hDC,
lpdis
-> rcItem.left,
lpdis
-> rcItem.top,
lpdis
-> rcItem.right - lpdis -> rcItem.left,
lpdis
-> rcItem.bottom - lpdis -> rcItem.top,
hdcBmp,
IMG_EXIT_ENABLE.left,
IMG_EXIT_ENABLE.top,
IMG_EXIT_ENABLE.right
- IMG_EXIT_ENABLE.left,
IMG_EXIT_ENABLE.bottom
- IMG_EXIT_ENABLE.top,
DEFAULT_TRANSPARENT_COLOR);
}

if (hdcBmp != NULL && hOldSel != NULL)
{
SelectObject(hdcBmp,hOldSel);
DeleteDC(hdcBmp);

hdcBmp
= NULL;
hOldSel
= NULL;
}

if (hBmp != NULL)
{
DeleteObject(hBmp);
hBmp
= NULL;
}

// Drawthetext
SetBkMode(lpdis -> hDC,TRANSPARENT);
DrawText(lpdis
-> hDC,m_pszBnExit, - 1 , & lpdis -> rcItem,DT_CENTER | DT_VCENTER);

}

// -----------------------------------------------------------------------------------------
// Description:
// OnthemessageWM_COMMANDandtheHIWORD(wParam)isBN_CLICKEDforthebuttonexit
//
// ----------------------------------------------------------------------------------------------
void CMainWnd::OnClickedBtnExit(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
DestroyWindow(m_hWnd);
PostQuitMessage(
0x00 );
}



// -----------------------------------------------------------------------------------------
// Description:
// OnthemessageWM_PAINT.
//
// ----------------------------------------------------------------------------------------------
void CMainWnd::OnPaint(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
RECTrcWnd
= { 0 };
GetWindowRect(hWnd,
& rcWnd);

PAINTSTRUCTps;
HDChdc
= BeginPaint(hWnd, & ps);
if (m_hdcBufBk == NULL)
{

// CreatethememoryDCforstoringthebackground.
m_hBmpBufBk = CreateCompatibleBitmap(hdc,rcWnd.right - rcWnd.left,rcWnd.bottom - rcWnd.top);
m_hdcBufBk
= CreateCompatibleDC(hdc);
m_hOldSelBufBk
= SelectObject(m_hdcBufBk,m_hBmpBufBk);


// Drawthebackground
HPENhPen = CreatePen(PS_SOLID, 1 ,RGB( 0 , 255 , 0 ));
HPENhOldPen
= NULL;
hOldPen
= (HPEN)SelectObject(m_hdcBufBk,hPen);
// therectcolor
HBRUSHhBrush = CreateSolidBrush(RGB( 0 , 255 , 0 ));
HGDIOBJhOldBrush
= SelectObject(m_hdcBufBk,hBrush);
// Draw
Rectangle(m_hdcBufBk, 0 , 0 ,rcWnd.right - rcWnd.left + 1 ,rcWnd.bottom - rcWnd.top + 1 );
// Realsetheresource
SelectObject(m_hdcBufBk,hOldBrush);
DeleteObject(hBrush);
SelectObject(m_hdcBufBk,hOldPen);
DeleteObject(hPen);
}


BitBlt(hdc,
rcWnd.left,
rcWnd.top,
rcWnd.right
- rcWnd.left,
rcWnd.bottom
- rcWnd.top,
m_hdcBufBk,
rcWnd.left,
rcWnd.top,
SRCCOPY);

EndPaint(hWnd,
& ps);

UpdateWindow(m_hBnExit);




}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值