MFC下的CBitmapButton我觉得是牛脾气的难用,WTL的这个不错,完全自绘,使用也简单方便
现在改装好并结合MFC的方式,下附源码及使用例子:
代码移植于WTL的CBitmapButton,可以在MFC下完美的使用
SkinButton.h
#pragma once // CSkinButton // bitmap button extended styles #define BMPBTN_HOVER 0x00000001 #define BMPBTN_AUTO3D_SINGLE 0x00000002 #define BMPBTN_AUTO3D_DOUBLE 0x00000004 #define BMPBTN_AUTOSIZE 0x00000008 #define BMPBTN_SHAREIMAGELISTS 0x00000010 #define BMPBTN_AUTOFIRE 0x00000020 class CSkinButton : public CButton { DECLARE_DYNAMIC(CSkinButton) public: enum { _nImageNormal = 0, _nImagePushed, _nImageFocusOrHover, _nImageDisabled, _nImageCount = 4, }; enum { ID_TIMER_FIRST = 1000, ID_TIMER_REPEAT = 1001 }; // Bitmap button specific extended styles DWORD m_dwExtendedStyle; CImageList m_ImageList; int m_nImage[_nImageCount]; CToolTipCtrl m_tip; LPTSTR m_lpstrToolTipText; // Internal states unsigned m_fMouseOver:1; unsigned m_fFocus:1; unsigned m_fPressed:1; // Constructor/Destructor CSkinButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL); virtual ~CSkinButton(); // Attributes DWORD GetBitmapButtonExtendedStyle() const; DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0); HIMAGELIST GetImageList() const; HIMAGELIST SetImageList(HIMAGELIST hImageList); int GetToolTipTextLength() const; bool GetToolTipText(LPTSTR lpstrText, int nLength) const; bool SetToolTipText(LPCTSTR lpstrText); // Operations void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1); BOOL SizeToImage(); BOOL ResizeClient(int nWidth, int nHeight, BOOL bRedraw = TRUE); // Overrideables virtual void DoPaint(CDC *pdc); // Implementation void Init(); BOOL StartTrackMouseLeave(); bool IsHoverMode() const; protected: DECLARE_MESSAGE_MAP() virtual void PreSubclassWindow(); public: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam); afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnKillFocus(CWnd* pNewWnd); afx_msg void OnSetFocus(CWnd* pOldWnd); virtual BOOL PreTranslateMessage(MSG* pMsg); afx_msg void OnDestroy(); afx_msg void OnPaint(); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnCaptureChanged(CWnd *pWnd); afx_msg void OnEnable(BOOL bEnable); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/); };
SkinButton.cpp
// SkinButton.cpp : implementation file // #include "stdafx.h" #include "SkinButton.h" // CSkinButton IMPLEMENT_DYNAMIC(CSkinButton, CButton) CSkinButton::CSkinButton(DWORD dwExtendedStyle/* = BMPBTN_AUTOSIZE*/, HIMAGELIST hImageList/* = NULL*/) : m_dwExtendedStyle(dwExtendedStyle), m_lpstrToolTipText(NULL), m_fMouseOver(0), m_fFocus(0), m_fPressed(0) { m_ImageList.Attach(hImageList); m_nImage[_nImageNormal] = -1; m_nImage[_nImagePushed] = -1; m_nImage[_nImageFocusOrHover] = -1; m_nImage[_nImageDisabled] = -1; } CSkinButton::~CSkinButton() { if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0) m_ImageList.DeleteImageList(); delete [] m_lpstrToolTipText; } BEGIN_MESSAGE_MAP(CSkinButton, CButton) ON_WM_CREATE() ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_TIMER() ON_WM_KEYDOWN() ON_WM_KEYUP() ON_WM_KILLFOCUS() ON_WM_SETFOCUS() ON_WM_DESTROY() ON_WM_PAINT() ON_WM_ERASEBKGND() ON_WM_CAPTURECHANGED() ON_WM_ENABLE() ON_WM_LBUTTONUP() ON_WM_LBUTTONDBLCLK() ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave) ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover) END_MESSAGE_MAP() // CSkinButton message handlers void CSkinButton::PreSubclassWindow() { CButton::PreSubclassWindow(); } // Attributes DWORD CSkinButton::GetBitmapButtonExtendedStyle() const { return m_dwExtendedStyle; } DWORD CSkinButton::SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask/* = 0*/) { DWORD dwPrevStyle = m_dwExtendedStyle; if(dwMask == 0) m_dwExtendedStyle = dwExtendedStyle; else m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask); return dwPrevStyle; } HIMAGELIST CSkinButton::GetImageList() const { return m_ImageList; } HIMAGELIST CSkinButton::SetImageList(HIMAGELIST hImageList) { HIMAGELIST hImageListPrev = m_ImageList; m_ImageList.m_hImageList = hImageList; if((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0 && ::IsWindow(m_hWnd)) SizeToImage(); return hImageListPrev; } int CSkinButton::GetToolTipTextLength() const { return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText); } bool CSkinButton::GetToolTipText(LPTSTR lpstrText, int nLength) const { ATLASSERT(lpstrText != NULL); if(m_lpstrToolTipText == NULL) return false; errno_t nRet; #ifdef _UNICODE nRet = wcsncpy_s(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE); #else nRet = strncpy_s(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE); #endif return (nRet == 0 || nRet == STRUNCATE); } bool CSkinButton::SetToolTipText(LPCTSTR lpstrText) { if(m_lpstrToolTipText != NULL) { delete [] m_lpstrToolTipText; m_lpstrToolTipText = NULL; } if(lpstrText == NULL) { if(m_tip.m_hWnd) m_tip.Activate(FALSE); return true; } int cchLen = lstrlen(lpstrText) + 1; ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]); if(m_lpstrToolTipText == NULL) return false; #ifdef _UNICODE wcscpy(m_lpstrToolTipText,lpstrText); #else strcpy(m_lpstrToolTipText,lpstrText); #endif if(m_tip.m_hWnd) { m_tip.Activate(TRUE); m_tip.AddTool(this, m_lpstrToolTipText); } return true; } // Operations void CSkinButton::SetImages(int nNormal, int nPushed /*= -1*/, int nFocusOrHover/* = -1*/, int nDisabled /*= -1*/) { if(nNormal != -1) m_nImage[_nImageNormal] = nNormal; if(nPushed != -1) m_nImage[_nImagePushed] = nPushed; if(nFocusOrHover != -1) m_nImage[_nImageFocusOrHover] = nFocusOrHover; if(nDisabled != -1) m_nImage[_nImageDisabled] = nDisabled; } BOOL CSkinButton::SizeToImage() { ATLASSERT(::IsWindow(m_hWnd) && m_ImageList.m_hImageList != NULL); int cx = 0; int cy = 0; if(!::ImageList_GetIconSize(m_ImageList.m_hImageList, &cx, &cy)) return FALSE; return ResizeClient(cx, cy); } BOOL CSkinButton::ResizeClient(int nWidth, int nHeight, BOOL bRedraw/* = TRUE*/) { ASSERT(::IsWindow(m_hWnd)); CRect rcWnd; GetClientRect(&rcWnd); if (rcWnd.IsRectNull()) return FALSE; if(nWidth != -1) rcWnd.right = nWidth; if(nHeight != -1) rcWnd.bottom = nHeight; if(!::AdjustWindowRectEx(&rcWnd, GetStyle(), (!(GetStyle() & WS_CHILD) && (GetMenu() != NULL)), GetExStyle())) return FALSE; UINT uFlags = SWP_NOZORDER | SWP_NOMOVE; if(!bRedraw) uFlags |= SWP_NOREDRAW; return SetWindowPos(NULL, 0, 0, rcWnd.right - rcWnd.left, rcWnd.bottom - rcWnd.top, uFlags); } // Overrideables void CSkinButton::DoPaint(CDC *pdc) { ASSERT(m_ImageList.m_hImageList != NULL); // image list must be set ASSERT(m_nImage[0] != -1); // main bitmap must be set // set bitmap according to the current button state int nImage = -1; bool bHover = IsHoverMode(); if(!IsWindowEnabled()) nImage = m_nImage[_nImageDisabled]; else if(m_fPressed == 1) nImage = m_nImage[_nImagePushed]; else if((!bHover && m_fFocus == 1) || (bHover && m_fMouseOver == 1)) nImage = m_nImage[_nImageFocusOrHover]; if(nImage == -1) // not there, use default one nImage = m_nImage[_nImageNormal]; // draw the button image int xyPos = 0; if((m_fPressed == 1) && ((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0) && (m_nImage[_nImagePushed] == -1)) xyPos = 1; POINT pt={xyPos, xyPos}; m_ImageList.Draw(pdc, nImage,pt, ILD_NORMAL); // draw 3D border if required if((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0) { RECT rect; GetClientRect(&rect); if(m_fPressed == 1) pdc->DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT); else if(!bHover || m_fMouseOver == 1) pdc->DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT); if(!bHover && m_fFocus == 1) { ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE)); pdc->DrawFocusRect(&rect); } } CString strText; GetWindowText(strText); if (!strText.IsEmpty()) { //create font CFont font; TCHAR sFontFace[] = TEXT("宋体"); LOGFONT lf; ZeroMemory( &lf, sizeof(lf) ); lf.lfHeight = 13; lf.lfWeight = FW_NORMAL; lf.lfCharSet = DEFAULT_CHARSET; lf.lfQuality = DEFAULT_QUALITY; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE; _tcscpy( lf.lfFaceName, sFontFace ); font.CreateFontIndirect(&lf); CFont *pOldFont = pdc->SelectObject(&font); RECT rect; GetClientRect(&rect); pdc->SetBkMode(TRANSPARENT); pdc->SetTextColor(RGB(0,0,0)); pdc->DrawText(strText,strText.GetLength(),&rect,DT_CENTER|DT_SINGLELINE|DT_VCENTER); pdc->SelectObject(pOldFont); } } // Implementation void CSkinButton::Init() { // We need this style to prevent Windows from painting the button ModifyStyle(0, BS_OWNERDRAW); // create a tool tip m_tip.Create(this); ASSERT(m_tip.m_hWnd); if(m_tip.m_hWnd && m_lpstrToolTipText != NULL) { m_tip.Activate(TRUE); m_tip.AddTool(this, m_lpstrToolTipText); } if(m_ImageList.m_hImageList != NULL && (m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0) SizeToImage(); } BOOL CSkinButton::StartTrackMouseLeave() { TRACKMOUSEEVENT tme = { 0 }; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = m_hWnd; return _TrackMouseEvent(&tme); } bool CSkinButton::IsHoverMode() const { return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0); } int CSkinButton::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CButton::OnCreate(lpCreateStruct) == -1) return -1; Init(); return 0; } void CSkinButton::OnLButtonDown(UINT nFlags, CPoint point) { LRESULT lRet = 0; if(IsHoverMode()) SetCapture(); else CButton::OnLButtonDown(nFlags, point); if(::GetCapture() == m_hWnd) { m_fPressed = 1; Invalidate(); UpdateWindow(); } if((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) { int nElapse = 250; int nDelay = 0; if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0)) nElapse += nDelay * 250; // all milli-seconds SetTimer(ID_TIMER_FIRST, nElapse,NULL); } } void CSkinButton::OnMouseMove(UINT nFlags, CPoint point) { if(::GetCapture() == m_hWnd) { POINT ptCursor = point; ClientToScreen(&ptCursor); RECT rect = { 0 }; GetWindowRect(&rect); unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0; if(m_fPressed != uPressed) { m_fPressed = uPressed; Invalidate(); UpdateWindow(); } } else if(IsHoverMode() && m_fMouseOver == 0) { m_fMouseOver = 1; Invalidate(); UpdateWindow(); StartTrackMouseLeave(); } CButton::OnMouseMove(nFlags, point); } LRESULT CSkinButton::OnMouseLeave(WPARAM wParam, LPARAM lParam) { if(m_fMouseOver == 1) { m_fMouseOver = 0; Invalidate(); UpdateWindow(); } return 0; } LRESULT CSkinButton::OnMouseHover(WPARAM wParam, LPARAM lParam) { return 0; } void CSkinButton::OnTimer(UINT_PTR nIDEvent) { ASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0); switch(nIDEvent) // timer ID { case ID_TIMER_FIRST: KillTimer(ID_TIMER_FIRST); if(m_fPressed == 1) { GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd); int nElapse = 250; int nRepeat = 40; if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0)) nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated SetTimer(ID_TIMER_REPEAT, nElapse,NULL); } break; case ID_TIMER_REPEAT: if(m_fPressed == 1) GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd); else if(::GetCapture() != m_hWnd) KillTimer(ID_TIMER_REPEAT); break; default: // not our timer break; } CButton::OnTimer(nIDEvent); } void CSkinButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if(nChar == VK_SPACE && IsHoverMode()) return; // ignore if in hover mode if(nChar == VK_SPACE && m_fPressed == 0) { m_fPressed = 1; Invalidate(); UpdateWindow(); } CButton::OnKeyDown(nChar, nRepCnt, nFlags); } void CSkinButton::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { if(nChar == VK_SPACE && IsHoverMode()) return; // ignore if in hover mode if(nChar == VK_SPACE && m_fPressed == 1) { m_fPressed = 0; Invalidate(); UpdateWindow(); } CButton::OnKeyUp(nChar, nRepCnt, nFlags); } void CSkinButton::OnKillFocus(CWnd* pNewWnd) { CButton::OnKillFocus(pNewWnd); m_fFocus = 0; Invalidate(); UpdateWindow(); } void CSkinButton::OnSetFocus(CWnd* pOldWnd) { CButton::OnSetFocus(pOldWnd); m_fFocus = 1; Invalidate(); UpdateWindow(); } BOOL CSkinButton::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if (pMsg->message==WM_LBUTTONDBLCLK) { pMsg->message=WM_LBUTTONDOWN; } if(m_tip.m_hWnd) m_tip.RelayEvent(pMsg); return CButton::PreTranslateMessage(pMsg); } void CSkinButton::OnDestroy() { CButton::OnDestroy(); if(m_tip.m_hWnd) { m_tip.DestroyWindow(); m_tip.m_hWnd = NULL; } } void CSkinButton::OnPaint() { CPaintDC dc(this); DoPaint(&dc); } BOOL CSkinButton::OnEraseBkgnd(CDC* pDC) { return TRUE; } void CSkinButton::OnCaptureChanged(CWnd *pWnd) { // TODO: Add your message handler code here if(m_fPressed == 1) { m_fPressed = 0; Invalidate(); UpdateWindow(); } CButton::OnCaptureChanged(pWnd); } void CSkinButton::OnEnable(BOOL bEnable) { CButton::OnEnable(bEnable); Invalidate(); UpdateWindow(); } void CSkinButton::OnLButtonUp(UINT nFlags, CPoint point) { LRESULT lRet = 0; bool bHover = IsHoverMode(); if (!bHover) CButton::OnLButtonUp(nFlags, point); if(::GetCapture() == m_hWnd) { if(bHover && m_fPressed == 1) GetParent()->PostMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd); ::ReleaseCapture(); } } void CSkinButton::OnLButtonDblClk(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default LRESULT lRet = 0; if(!IsHoverMode()) CButton::OnLButtonDblClk(nFlags, point); if(::GetCapture() != m_hWnd) SetCapture(); if(m_fPressed == 0) { m_fPressed = 1; Invalidate(); UpdateWindow(); } } void CSkinButton::DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/) { // TODO: Add your code to draw the specified item }
使用方法:
CImageList imageList; imageList.Attach(ImageList_LoadImage(AfxGetResourceHandle(),MAKEINTRESOURCE(IDB_LOGONBTN), 76, 4,CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION)); m_logonbtn.SetImageList(imageList); m_logonbtn.SetImages(0, 1, 2, 3); m_logonbtn.SetBitmapButtonExtendedStyle(BMPBTN_HOVER, BMPBTN_HOVER); m_logonbtn.SetToolTipText(_T("登陆")); imageList.Detach();
效果: