本文使用vc6.0 MFC程序实现自定义的多状态button。vc9.0(即vs2008)下,去掉stdafx.h文件中的#define _WIN32_WINNT 0x0400 也可以运行(有个warning)。
由于只是示例,并没有注意接口,您可以自己改;自己的美工水平不行,您可以自己更改bitmap资源。
介绍了三种多状态按钮 :
1)三状态按钮
鼠标在button上;鼠标在button外;鼠标按下button
2)六状态按钮
在1)的基础上增加了对应的获得焦点的三种情况
3)MFC自带的CBitmapButton类
有四种状态:up,down,focus,disabled。控件风格不同,状态个数不同,具体可看msdn。
代码可到此处下载:
http://download.csdn.net/source/986965
(一)三状态按钮
1)定义继承自CButton的子类CyctBitmapButton。
2)三状态需要三个位图资源
装在和卸载位图资源:
int CyctBitmapButton::Init() { //(窗口建立后的)初始化 m_hIn = ::LoadBitmap(::AfxGetInstanceHandle(),MAKEINTRESOURCE(IDB_BITMAP_IN)); m_hOut = ::LoadBitmap(::AfxGetInstanceHandle(),MAKEINTRESOURCE(IDB_BITMAP_OUT)); m_hDown = ::LoadBitmap(::AfxGetInstanceHandle(),MAKEINTRESOURCE(IDB_BITMAP_DOWN)); ASSERT(m_hIn != NULL); ASSERT(m_hOut != NULL); ASSERT(m_hDown != NULL); //SetButtonStyle( BS_BITMAP | GetButtonStyle() ); ModifyStyle(0,BS_BITMAP);//增加BS_BITMAP风格 SetBitmap(m_hOut); return 0; } void CyctBitmapButton::OnDestroy() { CButton::OnDestroy(); //卸载Bitmap资源 #ifdef _DEBUG ASSERT(0 != DeleteObject(m_hIn)); ASSERT(0 != DeleteObject(m_hOut)); ASSERT(0 != DeleteObject(m_hDown)); #else DeleteObject(m_hIn); DeleteObject(m_hOut); DeleteObject(m_hPutdown); #endif }
3)初始化button
窗口初始化后需要调用Init()函数。布尔值变量m_fIsInit确保button只初始化一次。
void CyctBitmapButton::PreSubclassWindow() { //初始化 if(!m_fIsInit) { //AfxMessageBox(_T("CyctBitmapButton::PreSubclassWindow")); Init(); m_fIsInit = TRUE; } CButton::PreSubclassWindow(); } int CyctBitmapButton::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CButton::OnCreate(lpCreateStruct) == -1) return -1; //初始化 if(!m_fIsInit) { //AfxMessageBox(_T("CyctBitmapButton::OnCreate")); Init(); m_fIsInit = TRUE; } return 0; }
4)处理鼠标响应函数(这是关键的)
处理了四种鼠标消息WM_MOUSEMOVE,WM_LBUTTONDOWN,WM_LBUTTONUP,WM_MOUSELEAVE:
void CyctBitmapButton::OnMouseMove(UINT nFlags, CPoint point) { if(m_MouseState.GetCurState() == _mouse_out) { SetBitmap(m_hIn); return; } //当鼠标离开时触发WM_MOUSELEAVE事件 TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = m_hWnd; tme.dwHoverTime = HOVER_DEFAULT; ::TrackMouseEvent(&tme); CButton::OnMouseMove(nFlags, point); } void CyctBitmapButton::OnLButtonDown(UINT nFlags, CPoint point) { SetBitmap(m_hDown); CButton::OnLButtonDown(nFlags, point); } void CyctBitmapButton::OnLButtonUp(UINT nFlags, CPoint point) { SetBitmap(m_hIn); CButton::OnLButtonUp(nFlags, point); } LRESULT CyctBitmapButton::OnMouseLeave(WPARAM wParam, LPARAM lParam) { SetBitmap(m_hOut); return 0; }
其中WM_MOUSELEAVE消息响应映射是手工添加的。
在头文件中DECLARE_MESSAGE_MAP()前添加一句:
afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);//手工添加
在对应的cpp文件中BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加一句:
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)//手工添加
WM_MOUSELEAVE消息是由函数TrackMouseEvent产生的,当鼠标离开button控件就会触发此消息。
函数SetBitmap定义如下(具体的看源代码吧):
HBITMAP CyctBitmapButton::SetBitmap(HBITMAP hBitmap) { //设置按钮的位图 ASSERT(m_hIn == hBitmap || m_hOut == hBitmap || m_hDown == hBitmap); if(m_hIn == hBitmap) m_MouseState.SetCurState( _mouse_in ); else if(m_hOut == hBitmap) m_MouseState.SetCurState(_mouse_out ); else if(m_hDown == hBitmap) m_MouseState.SetCurState( _mouse_down ); return CButton::SetBitmap(hBitmap); }
5)如果想去掉按钮button在获得焦点时的虚框,重载WM_SETFOCUS消息,并且置函数体为空即可。
(二)六状态按钮
与(一)类似,不再叙述。
(三)MFC自带的CBitmapButton类
使用很简单,在对话框类的OnInitDialog内装载四个位图资源即可:
//初始化CBitmapButton m_BitmapButton.LoadBitmaps(IDB_BITMAP_UP,IDB_BITMAP_DOWN2 ,IDB_BITMAP_FOCUS,IDB_BITMAP_DISABLED);
总结:多状态按钮主要是使用函数TrackMouseEvent函数实现的。本文只是示例,很简陋。你也可以下载CButtonST来看看,方法基本一样。下载区有:
http://search.download.csdn.net/search/cbuttonst