在操手MFC的时候,经常会抱怨MFC界面不如其他的框架或语言,比如VB,C#等等,面对MS在系统上的不断更新换代,我们也越来越追求软件的视觉效果,譬如我们会更喜欢win7下的玻璃效果,看起来很炫。
在接触MFC的时候,实在忍不住对MFC的bulider的仰慕之情,因为他把之前的win32 c编程(注意,里面没有面向对象)和C++有机的结合起来了(这时OO跑进来了),让我们更多的专注于软件的功能设计方面,减少了程序繁杂(真心真诚的佩服win32走过来的那些来程序员们)。
每天都有新的收获,当然不仅仅是软件方面的,因为最近讲师在教我们设计自己的U,其实这个看起来还是蛮有难度的,实际上也很有难度,但是讲师说的没错,了解硬件上的架构只是,或许对于我们软件方面的同学来说,也是不无益处的。
上主题吧。
CXPButton类是一个很不错的按钮类,代码阅读起来也很简单,不是非常难,只要是我们对windows编程有所了解的话。难点也不多,主要是对鼠标消息的处理上,比如鼠标到了按钮的上面但是没有按下是什么情形,鼠标按钮按下了又是什么情形,这个都是要体现出来的呀,不然人机交互就苦难了,大众也不会喜欢的。
另外我觉得在美术的设计上也是挺有难度的,就是很多都知道什么什么样的东西看起来很酷,但是就不知道它怎么去设计,就是所谓的知其然而不起所以然。
还有在消息的阅读上也有难度,比较生疏的地方就是,WM_MOUSELEAVE和WM_MOUSEHOVER这两个鼠标消息,windows默认是不处理这两个消息的,但是在自定义按钮过程当中又要用到它们。
所以要触发它们。
TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.hwndTrack = m_hWnd; tme.dwFlags = TME_LEAVE | TME_HOVER; tme.dwHoverTime = 1; m_bTracking = _TrackMouseEvent(&tme);
上面的代码就是我们所需要的。当鼠标在按钮上停留或者走出按钮区域的话,就(才)会触发两个消息。
在CXPButton当中只要我们熟练如何运用GDI(再在加上一点点的艺术天分),便可以做出非常酷的按钮。
下面是源代码:
#if !defined(AFX_XPBUTTON_H__44CD5B2A_756E_4939_9261_E0034E0F2DEF__INCLUDED_) #define AFX_XPBUTTON_H__44CD5B2A_756E_4939_9261_E0034E0F2DEF__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // XPButton.h : header file // ///////////////////////////////////////////////////////////////////////////// // CXPButton window class CXPButton : public CButton { // Construction public: CXPButton(); // Attributes protected: //按钮的外边框 CPen m_BoundryPen; //鼠标指针置于按钮之上时按钮的内边框 CPen m_InsideBoundryPenLeft; CPen m_InsideBoundryPenRight; CPen m_InsideBoundryPenTop; CPen m_InsideBoundryPenBottom; //按钮获得焦点时按钮的内边框 CPen m_InsideBoundryPenLeftSel; CPen m_InsideBoundryPenRightSel; CPen m_InsideBoundryPenTopSel; CPen m_InsideBoundryPenBottomSel; //按钮的底色,包括有效和无效两种状态 CBrush m_FillActive; CBrush m_FillInactive; //按钮的状态 BOOL m_bOver; //鼠标位于按钮之上时该值为true,反之为flase BOOL m_bTracking; //在鼠标按下没有释放时该值为true BOOL m_bSelected; //按钮被按下是该值为true BOOL m_bFocus; //按钮为当前焦点所在时该值为true // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CXPButton) protected: virtual void PreSubclassWindow(); //}}AFX_VIRTUAL // Implementation public: virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); virtual void DoGradientFill(CDC *pDC, CRect* rect); virtual void DrawInsideBorder(CDC *pDC, CRect* rect); virtual ~CXPButton(); // Generated message map functions protected: //{{AFX_MSG(CXPButton) 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 DECLARE_MESSAGE_MAP() }; ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_XPBUTTON_H__44CD5B2A_756E_4939_9261_E0034E0F2DEF__INCLUDED_)
// XPButton.cpp : implementation file // #include "stdafx.h" #include "XPButton.h" #include "resource.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CXPButton CXPButton::CXPButton() { m_BoundryPen.CreatePen(PS_INSIDEFRAME | PS_SOLID, 1, RGB(0, 0, 0)); m_InsideBoundryPenLeft.CreatePen(PS_INSIDEFRAME | PS_SOLID, 3, RGB(250, 196, 88)); m_InsideBoundryPenRight.CreatePen(PS_INSIDEFRAME | PS_SOLID, 3, RGB(251, 202, 106)); m_InsideBoundryPenTop.CreatePen(PS_INSIDEFRAME | PS_SOLID, 2, RGB(252, 210, 121)); m_InsideBoundryPenBottom.CreatePen(PS_INSIDEFRAME | PS_SOLID, 2, RGB(229, 151, 0)); m_FillActive.CreateSolidBrush(RGB(223, 222, 236)); m_FillInactive.CreateSolidBrush(RGB(222, 223, 236)); m_InsideBoundryPenLeftSel.CreatePen(PS_INSIDEFRAME | PS_SOLID, 3, RGB(153, 198, 252)); m_InsideBoundryPenTopSel.CreatePen(PS_INSIDEFRAME | PS_SOLID, 2, RGB(162, 201, 255)); m_InsideBoundryPenRightSel.CreatePen(PS_INSIDEFRAME | PS_SOLID, 3, RGB(162, 189, 252)); m_InsideBoundryPenBottomSel.CreatePen(PS_INSIDEFRAME | PS_SOLID, 2, RGB(162, 201, 255)); m_bOver = m_bSelected = m_bTracking = m_bFocus = FALSE; } CXPButton::~CXPButton() { m_BoundryPen.DeleteObject(); m_InsideBoundryPenLeft.DeleteObject(); m_InsideBoundryPenRight.DeleteObject(); m_InsideBoundryPenTop.DeleteObject(); m_InsideBoundryPenBottom.DeleteObject(); m_FillActive.DeleteObject(); m_FillInactive.DeleteObject(); m_InsideBoundryPenLeftSel.DeleteObject(); m_InsideBoundryPenTopSel.DeleteObject(); m_InsideBoundryPenRightSel.DeleteObject(); m_InsideBoundryPenBottomSel.DeleteObject(); } BEGIN_MESSAGE_MAP(CXPButton, CButton) //{{AFX_MSG_MAP(CXPButton) ON_WM_MOUSEMOVE() ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave) ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CXPButton message handlers //添加Owner Draw属性 void CXPButton::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class CButton::PreSubclassWindow(); ModifyStyle(0, BS_OWNERDRAW); } void CXPButton::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default 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); } CButton::OnMouseMove(nFlags, point); } LRESULT CXPButton::OnMouseLeave(WPARAM wParam, LPARAM lParam) { m_bOver = FALSE; m_bTracking = FALSE; InvalidateRect(NULL, FALSE);/**/ return 0; } LRESULT CXPButton::OnMouseHover(WPARAM wParam, LPARAM lParam) { m_bOver = TRUE; InvalidateRect(NULL); return 0; } void CXPButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { //从lpDrawItemStruct获取控件的相关信息 CRect rect = lpDrawItemStruct->rcItem; CDC *pDC=CDC::FromHandle(lpDrawItemStruct->hDC); int nSaveDC=pDC->SaveDC(); UINT state = lpDrawItemStruct->itemState; POINT pt ; TCHAR strText[MAX_PATH + 1] = L"我爱你"; ::GetWindowText(m_hWnd, strText, MAX_PATH);/*获取文本原色*/ //画按钮的外边框,它是一个半径为5的圆角矩形 pt.x = 5; pt.y = 5; CPen* hOldPen = pDC->SelectObject(&m_BoundryPen); pDC->RoundRect(&rect, pt); //获取按钮的状态 if (state & ODS_FOCUS) { m_bFocus = TRUE; m_bSelected = TRUE; } else { m_bFocus = FALSE; m_bSelected = FALSE; } if (state & ODS_SELECTED || state & ODS_DEFAULT) { m_bFocus = TRUE; } pDC->SelectObject(hOldPen); rect.DeflateRect(CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE))); //根据按钮的状态填充按钮的底色 CBrush* pOldBrush; if (m_bOver) { pOldBrush = pDC->SelectObject(&m_FillActive); DoGradientFill(pDC, &rect); } else { pOldBrush = pDC->SelectObject(&m_FillInactive); DoGradientFill(pDC, &rect); } //根据按钮的状态绘制内边框 if (m_bOver || m_bSelected) DrawInsideBorder(pDC, &rect); pDC->SelectObject(pOldBrush); //显示按钮的文本 if (strText!=NULL) { CFont* hFont = GetFont(); CFont* hOldFont = pDC->SelectObject(hFont); CSize szExtent = pDC->GetTextExtent(strText, lstrlen(strText)); CPoint pt( rect.CenterPoint().x - szExtent.cx / 2, rect.CenterPoint().y - szExtent.cy / 2); if (state & ODS_SELECTED) pt.Offset(1, 1); int nMode = pDC->SetBkMode(TRANSPARENT); if (state & ODS_DISABLED) pDC->DrawState(pt, szExtent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL); else pDC->DrawState(pt, szExtent, strText, DSS_NORMAL, TRUE, 0, (HBRUSH)NULL); pDC->SelectObject(hOldFont); pDC->SetBkMode(nMode); } pDC->RestoreDC(nSaveDC); } //绘制按钮的底色 void CXPButton::DoGradientFill(CDC *pDC, CRect* rect) { CBrush brBk[64]; int nWidth = rect->Width(); int nHeight = rect->Height(); CRect rct; /*for (int i = 0; i < 64; i ++) { if (m_bOver) { if (m_bFocus) brBk[i].CreateSolidBrush(RGB(255 - (i / 4), 255 - (i / 4), 255 - (i / 3))); else brBk[i].CreateSolidBrush(RGB(255 - (i / 4), 255 - (i / 4), 255 - (i / 5))); } else { if (m_bFocus) brBk[i].CreateSolidBrush(RGB(255 - (i / 3), 255 - (i / 3), 255 - (i / 4))); else brBk[i].CreateSolidBrush(RGB(255 - (i / 3), 255 - (i / 3), 255 - (i / 5))); } } int i; for (i = rect->top; i <= nHeight + 2; i ++) { rct.SetRect(rect->left, i, nWidth + 2, i + 1); pDC->FillRect(&rct, &brBk[((i * 63) / nHeight)]); }*/ int i; brBk[0].CreateSolidBrush(RGB(255,0,0)); brBk[1].CreateSolidBrush(RGB(0,0,0)); brBk[2].CreateSolidBrush(RGB(0,0,255)); brBk[3].CreateSolidBrush(RGB(0,255,0)); CBitmap bitmap; BITMAP bmp; bitmap.LoadBitmapW(IDB_QQ); bitmap.GetBitmap(&bmp); CDC MemDC; MemDC.CreateCompatibleDC(pDC); if(m_bOver) { CBitmap * OldBitmap = MemDC.SelectObject(&bitmap); pDC->BitBlt(0,0,bmp.bmWidth,bmp.bmHeight,&MemDC,0,0,SRCAND); MemDC.SelectObject(OldBitmap); /*if(m_bFocus)//鼠标+焦点 pDC ->FillRect(rect,&brBk[0]);//红 else//鼠标+无焦点 pDC ->FillRect(rect,&brBk[1]);//黑*/ } else { CBitmap * OldBitmap = MemDC.SelectObject(&bitmap); pDC->StretchBlt(0,0,bmp.bmWidth+10,bmp.bmHeight+10,&MemDC, 0,0,bmp.bmWidth,bmp.bmHeight,SRCAND); MemDC.SelectObject(OldBitmap); /*if(m_bFocus)//无鼠标+焦点 pDC ->FillRect(rect,&brBk[2]);//蓝 else pDC ->FillRect(rect,&brBk[3]);//绿*/ } /*for (i = 0; i< 64; i ++) brBk[i].DeleteObject();*/ } //绘制按钮的内边框 void CXPButton::DrawInsideBorder(CDC *pDC, CRect* rect) { CPen *pLeft, *pRight, *pTop, *pBottom; if (m_bSelected && !m_bOver) { pLeft = & m_InsideBoundryPenLeftSel; pRight = &m_InsideBoundryPenRightSel; pTop = &m_InsideBoundryPenTopSel; pBottom = &m_InsideBoundryPenBottomSel; } else { pLeft = &m_InsideBoundryPenLeft; pRight = &m_InsideBoundryPenRight; pTop = &m_InsideBoundryPenTop; pBottom = &m_InsideBoundryPenBottom; } CPoint oldPoint = pDC->MoveTo(rect->left, rect->bottom - 1); CPen* pOldPen = pDC->SelectObject(pLeft); pDC->LineTo(rect->left, rect->top + 1); pDC->SelectObject(pRight); pDC->MoveTo(rect->right - 1, rect->bottom - 1); pDC->LineTo(rect->right - 1, rect->top); pDC->SelectObject(pTop); pDC->MoveTo(rect->left - 1, rect->top); pDC->LineTo(rect->right - 1, rect->top); pDC->SelectObject(pBottom); pDC->MoveTo(rect->left, rect->bottom); pDC->LineTo(rect->right - 1, rect->bottom); pDC->SelectObject(pOldPen); pDC->MoveTo(oldPoint); if (m_bSelected && !m_bOver) DrawFocusRect(pDC->m_hDC,rect); }