我们要举的例子是利用一张包含按钮的三种状态(鼠标移动,鼠标离开,鼠标单击)的位图来绘制按钮,因为三种状态
在一张位图上,所以每种状态的图片高度都相等,而宽度是位图总长度的1/3。
1.首先创建一个CBitmapButton的子类CHoverButton,并创建四个类成员变量:
//指示鼠标是否在按钮上面
BOOL m_bHover;
//按钮是否跟踪到鼠标
BOOL m_bTracking;
//保存图片的变量
CBitmap mybitmap;
//按钮尺寸
CSize m_ButtonSize;
2.在类的构造函数中,初始化和鼠标相关的变量
CHoverButton::CHoverButton()
{
m_bHover = FALSE;
m_bTracking = FALSE;
}
3.创建一个载入位图的成员函数,参数为位图的资源标识符。在按钮自绘之前,必须有相应的位图已经载入。
BOOL LoadBitmap(UINT bitmapid);
其实现为:
BOOL CHoverButton::LoadBitmap(UINT bitmapid)
{
//载入图片
mybitmap.Attach(::LoadImage(::AfxGetInstanceHandle(),MAKEINTRESOURCE(bitmapid), IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS));
BITMAP bitmapbits;
//获取位图信息并存入bitmapbits结构中
mybitmap.GetBitmap(&bitmapbits);
//取位图相应的高度和1/3宽度。
m_ButtonSize.cy=bitmapbits.bmHeight;
m_ButtonSize.cx=bitmapbits.bmWidth/3;
SetWindowPos( NULL, 0,0, m_ButtonSize.cx,m_ButtonSize.cy,SWP_NOMOVE |SWP_NOOWNERZORDER );
return TRUE;
}
4.重载按钮的虚拟函数DrawItem()成员函数
当一个自绘按钮的外观发生变化时由框架调用.其函数原型为:
virtual void DrawItem(
LPDRAWITEMSTRUCT lpDrawItemStruct
);
DRAWITEMSTRUCT结构包含被绘制项目的信息。
下面是该函数的实现
void CHoverButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
//获取保存在DRAWITEMSTRUCT结构中且在绘制按钮时必须使用的设备上下文
CDC* mydc=CDC::FromHandle(lpDrawItemStruct->hDC);
//创建兼容的设备上下文
CDC* pMemDC = new CDC;
pMemDC -> CreateCompatibleDC(mydc);
//保存旧对象
CBitmap * pOldBitmap;
pOldBitmap = pMemDC -> SelectObject(&mybitmap);
CPoint point(0,0);
//判断按钮是否处于选择状态,如果是则绘制选择状态的按钮位图,在我们提供的位图中,选中状态的按钮图片是第二个
if(lpDrawItemStruct->itemState & ODS_SELECTED)
{
mydc->BitBlt(0,0,m_ButtonSize.cx,m_ButtonSize.cy,pMemDC,m_ButtonSize.cx,0,SRCCOPY);
}
else
{ //判断鼠标是否离开还是在按钮上面,以便绘制相应的位图
if(m_bHover)
{
mydc->BitBlt(0,0,m_ButtonSize.cx,m_ButtonSize.cy,pMemDC,m_ButtonSize.cx*2,0,SRCCOPY);
}else
{
mydc->BitBlt(0,0,m_ButtonSize.cx,m_ButtonSize.cy,pMemDC,0,0,SRCCOPY);
}
}
// clean up
pMemDC -> SelectObject(pOldBitmap);
delete pMemDC;
}
5.对于按钮的鼠标移动处理,我们可以使用其WM_MOUSEMOVE消息来处理,而对于按钮的鼠标悬停和离开消息,我们必须使用
TrackMouseEvent()函数来处理,该函数在鼠标指针离开窗体或悬停在窗体上是发送消息.可以发送的消息有(WM_MOUSELEAVE,
WM_MOUSEHOVER,WM_NCMOUSELEAVE,WM_NCMOUSEHOVER)
该函数带有一个TRACKMOUSEEVENT结构参数,它包含跟踪鼠标的信息。
对于鼠标在按钮上移动,离开按钮,在按钮上悬停的处理代码如下:
void CHoverButton::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);
}
CBitmapButton::OnMouseMove(nFlags, point);
}
LRESULT CHoverButton::OnMouseLeave(WPARAM wparam, LPARAM lparam)
{
m_bTracking = FALSE;
m_bHover=FALSE;
//重画按钮
Invalidate(TRUE);
return 0;
}
LRESULT CHoverButton::OnMouseHover(WPARAM wparam, LPARAM lparam)
{
m_bHover=TRUE;
Invalidate(TRUE);
return 0;
}
6.我们把这个按钮放入对话框进行测试,首先在基于对话框的应用程序中加入一个按钮,设置其Owner Draw
的属性为true。然后添加一个按钮控件变量,然后用CHoverButton类代替CButton类
CHoverButton m_HoverButton;
最后在对话框的OnInitDialog()处理函数中加入下面一行代码来为按钮的自绘作准备:
m_HoverButton.LoadBitmap(IDB_BITMAP1);