天天看点

继承CWnd自绘按钮

时隔1年半后,因工作需要重新写起了客户端。这么久没接触了,确实很生疏,一考虑到要自绘个tab控件需要有个关闭按钮,刚开始我的逻辑就是用公司现成的按钮类但发现现成的按钮类,用起来很费劲,因为分装好的按钮类提供了比较完整的各种功能和接口,要是想在原来的基础上修改,就需要搞清楚各个成员及接口的用法,这加大了很大的难度,比起从零开始写,或许还更耗时,相信很多人都会有类似的感受,于是我网上搜索和参考了别人的文章封装了本按钮。本按钮提供的功能非常简单,但是易用且可扩展性强,完全可以根据自己的需求做修改.
           
//使用例子
RECT rc = {0, 0, 20, 20};
	m_btnClose.LoadBtnImg(_T("PNG"),IDB_PNG_TAB_CLOSE_NOR, IDB_PNG_TAB_CLOSE_HOT,IDB_PNG_TAB_CLOSE_HOT);
	m_btnClose.Create(this, rc, L"",ID_BTN_MYTAB_CLOSE);		
	m_btnClose.SetToolTipText(_T("关闭")); 		
           
//头文件

#pragma once

// CLhsButton

#define MYWM_BTN_CLICK WM_USER+3001		//关闭按钮单击响应

//tab按钮的状态
enum ButtonState
{
	BTN_STATE_NOR			= 0,
	BTN_STATE_DOWN			= 1,
};

class CLhsButton : public CWnd
{
	DECLARE_DYNAMIC(CLhsButton)

public:
	CLhsButton();
	virtual ~CLhsButton();

	 bool Create(CWnd* pParent,CRect rc,CString text,DWORD id = 0,DWORD style = WS_VISIBLE|WS_CHILD);  

	DECLARE_MESSAGE_MAP()

public:
	
protected:
	CString szClassName;  
	bool m_isMouseHover;		//鼠标是否悬浮
	bool m_isMouseClicked;		//鼠标是否单击
	CString m_strShowText;		//要显示的文字

	Image*						m_pImgNor;			//正常时的图片
	Image*						m_pImgHot;			//鼠标悬浮时的图片
	Image*						m_pImgDown;			//单击按下时的图片
	
	void PostClickEvent();
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);  
	afx_msg void OnMouseHover(UINT nFlags, CPoint point);  
	afx_msg void OnMouseLeave();  
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);  
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);  
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);  
	afx_msg void OnPaint();  
	afx_msg void OnSize(UINT nType, int cx, int cy);	
	virtual BOOL PreTranslateMessage(MSG* pMsg);
public:
	void SetTabState(ButtonState state){m_btnState = state; Invalidate();}	//设置tab状态
	ButtonState GetTabState(){return m_btnState;}
	void SetToolTipText(CString spText, BOOL bActivate = TRUE);
	void LoadBtnImg(LPCTSTR tType, UINT nNorID, UINT nHotID, UINT nDownID);		//加载按钮图片

private:
	ButtonState m_btnState;							//tab的状态	
	CToolTipCtrl*	m_pToolTip;
	CString m_tooltext;

public:
	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};

//源文件
// CLhsButton.cpp : 实现文件
//

#include "stdafx.h"
#include "../Lander_mini.h"
#include "memdc.h"
#include "../Utility.h"
#include "LhsButton.h"
// CLhsButton

IMPLEMENT_DYNAMIC(CLhsButton, CWnd)

CLhsButton::CLhsButton()
{
	m_isMouseHover = false;  
	m_isMouseClicked = false;  
	// 注册控件类  
	szClassName = AfxRegisterWndClass(0); 
	m_pImgNor = NULL;
	m_pImgHot = NULL;
	m_pImgDown = NULL;
	m_btnState = BTN_STATE_NOR;
	m_pToolTip = NULL;	
}

CLhsButton::~CLhsButton()
{
	SAFE_RELEASE(m_pToolTip);
	SAFE_RELEASE(m_pImgNor);
	SAFE_RELEASE(m_pImgHot);
	SAFE_RELEASE(m_pImgDown);
}


BEGIN_MESSAGE_MAP(CLhsButton, CWnd)
	ON_WM_MOUSEMOVE()  
	ON_WM_MOUSEHOVER()  // 此消息系统并不会给我们发送  
	ON_WM_MOUSELEAVE()  
	ON_WM_LBUTTONDOWN()  
	ON_WM_LBUTTONUP()  
	ON_WM_PAINT()  
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()  
	ON_WM_CREATE()	
	ON_WM_CTLCOLOR()
END_MESSAGE_MAP()

// CLhsButton 消息处理程序
bool CLhsButton::Create(CWnd* pParent,CRect rc,CString text,DWORD id /* = 0 */,DWORD style /* = WS_VISIBLE|WS_CHILD */)  
{  
	// 动态创建控件  	
	BOOL ret = CWnd::CreateEx(0, szClassName, text, style, rc, pParent, id);
	return ret ? true : false;  
}  

int CLhsButton::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	
	//::SetWindowLong(m_hWnd, GWL_EXSTYLE, GetWindowLong(m_hWnd, GWL_EXSTYLE) ^ WS_EX_LAYERED);
	
	//ModifyStyle(WS_CLIPCHILDREN, 0);
	//ModifyStyle(0, WS_CLIPCHILDREN , 0);

	return 0;
}

void CLhsButton::PostClickEvent()  
{  
	// 该函数用来向父窗口发送 单击 消息  
	CWnd* parent = GetParent();  
	if(parent)  
		parent->SendMessage(MYWM_BTN_CLICK, 0,0);  	
}  

void CLhsButton::OnMouseHover(UINT nFlags, CPoint point)  
{  
	// 鼠标进入  
	Invalidate();  
}  

void CLhsButton::OnMouseMove(UINT nFlags, CPoint point)
{
	// 只处理鼠标第一次进入时的情况  
	if(!m_isMouseHover)  
	{  
		m_isMouseHover = true;  

		TRACKMOUSEEVENT evt = { sizeof(evt), TME_LEAVE|TME_HOVER, m_hWnd, 0 };  
		TrackMouseEvent(&evt);  

		OnMouseHover(0,CPoint());  
	}  
}

void CLhsButton::OnMouseLeave()  
{  
	// 鼠标离开  
	m_isMouseHover = false;  
	m_isMouseClicked = false;  
	Invalidate();  
}  

void CLhsButton::OnLButtonDown(UINT nFlags, CPoint point)  
{  
	// 鼠标按下  
	m_isMouseClicked = true;  
	Invalidate();  
}  

void CLhsButton::OnLButtonUp(UINT nFlags, CPoint point)  
{  
	// 鼠标松开  
	if(m_isMouseClicked)  
	{  
		m_isMouseClicked = false;  
		Invalidate(); 
		PostClickEvent();  
	}  
}  

BOOL CLhsButton::OnEraseBkgnd(CDC* pDC)  
{  
//	return CWnd::OnEraseBkgnd(pDC);
	return TRUE;    // 阻止擦除背景,防止闪烁  
}  

void CLhsButton::OnPaint()  
{  
	CPaintDC dc(this);   
	CRect rc;  
	GetClientRect(&rc); 
	
	// 采用双缓存,防止闪烁  
	CMemDC memdc(&dc,&rc,TRUE);
	Graphics graphic(memdc);
	if (!m_pImgNor || !m_pImgHot || !m_pImgDown)
	{//没有提供按钮图片就刷下背景
		// 刷背景  
		COLORREF bkgnd = RGB(100,0,0);  
		if(m_isMouseHover)  
		{  
			if(m_isMouseClicked)  
				bkgnd = RGB(250,0,0);  
			else  
				bkgnd = RGB(180,0,0);  
		}  
		memdc.FillSolidRect(&rc,bkgnd);  
	}
	
	if (m_isMouseClicked || m_btnState == BTN_STATE_DOWN)
	{//单击一定画单击状态
		graphic.DrawImage(m_pImgDown, 0, 0, m_pImgDown->GetWidth(), m_pImgDown->GetHeight());
	}
	else if (m_isMouseHover && !m_isMouseClicked)
	{
		//悬浮,但是没单击
		graphic.DrawImage(m_pImgHot, 0, 0, m_pImgHot->GetWidth(), m_pImgHot->GetHeight());		
	}
	else
	{
		graphic.DrawImage(m_pImgNor, 0, 0, m_pImgNor->GetWidth(), m_pImgNor->GetHeight());	
	}
	
	// 使绘制生效  
	graphic.ReleaseHDC(memdc);				
}  


BOOL CLhsButton::PreTranslateMessage(MSG* pMsg) 
{
	if (m_pToolTip)
	{
		if (::IsWindow(m_pToolTip->m_hWnd))
		{
			m_pToolTip->RelayEvent(pMsg);		
		}
	}

	return CWnd::PreTranslateMessage(pMsg);
}


void CLhsButton::OnSize(UINT nType, int cx, int cy) 
{

}

void CLhsButton::SetToolTipText(CString spText, BOOL bActivate)
{
	if (m_pToolTip == NULL)
	{
		m_pToolTip = new CToolTipCtrl;
		// Create ToolTip control
		m_pToolTip->Create(this);
		m_pToolTip->Activate(TRUE);
	}

	m_tooltext = spText;

	// If there is no tooltip defined then add it
	if (m_pToolTip->GetToolCount() == 0)
	{
		CRect rectBtn; 
		GetClientRect(rectBtn);
		m_pToolTip->AddTool(this, m_tooltext, rectBtn, 1);
	}

	// Set text for tooltip
	m_pToolTip->UpdateTipText(m_tooltext, this, 1);
	m_pToolTip->SetDelayTime(2000);
	m_pToolTip->Activate(bActivate);
}


HBRUSH CLhsButton::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = CWnd::OnCtlColor(pDC, pWnd, nCtlColor);

	// TODO:  在此更改 DC 的任何属性

	// TODO:  如果默认的不是所需画笔,则返回另一个画笔
	return hbr;
}

//加载按钮图片
void CLhsButton::LoadBtnImg(LPCTSTR tType, UINT nNorID, UINT nHotID, UINT nDownID)
{
	m_pImgNor = CQYUtility::LoadImgFromRes(_T("PNG"), nNorID);
	m_pImgHot = CQYUtility::LoadImgFromRes(_T("PNG"), nHotID);
	m_pImgDown = CQYUtility::LoadImgFromRes(_T("PNG"), nDownID);	
}
           
<pre name="code" class="cpp">#ifndef _MEMDC_H_
#define _MEMDC_H_

//
// CMemDC - memory DC
//
class CMemDC : public CDC {
private:	
	CBitmap		m_bitmap;		// Offscreen bitmap
	CBitmap*	m_oldBitmap;	// bitmap originally found in CMemDC
	CDC*		m_pDC;			// Saves CDC passed in constructor
	CRect		m_rect;			// Rectangle of drawing area.
	BOOL		m_bMemDC;		// TRUE if CDC really is a Memory DC.
public:
	   void Build( CDC* pDC )
   {
        ASSERT(pDC != NULL); 

        m_pDC		= pDC;
        m_oldBitmap = NULL;
        m_bMemDC	= !pDC->IsPrinting();

        if( m_bMemDC )
		{
            CreateCompatibleDC(pDC);
            pDC->LPtoDP(&m_rect);

            m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
            m_oldBitmap = SelectObject(&m_bitmap);
            
            SetMapMode(pDC->GetMapMode());
            pDC->DPtoLP(&m_rect);
            SetWindowOrg(m_rect.left, m_rect.top);
        }
		else
		{
            m_bPrinting = pDC->m_bPrinting;
            m_hDC       = pDC->m_hDC;
            m_hAttribDC = pDC->m_hAttribDC;
        }

        FillSolidRect( m_rect, pDC->GetBkColor() );
    }
	
	CMemDC(CDC* pDC, const CRect* pRect = NULL, BOOL bBg = FALSE) : CDC()
	{
		ASSERT(pDC != NULL); 

		// Some initialization
		m_pDC = pDC;
		m_oldBitmap = NULL;
		m_bMemDC = !pDC->IsPrinting();

		// Get the rectangle to draw
		if (pRect == NULL) {
			pDC->GetClipBox(&m_rect);
		} else {
			m_rect = *pRect;
		}

		if (m_bMemDC) {
			// Create a Memory DC
			CreateCompatibleDC(pDC);
			pDC->LPtoDP(&m_rect);

			m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
			m_oldBitmap = SelectObject(&m_bitmap);

			SetMapMode(pDC->GetMapMode());

			SetWindowExt(pDC->GetWindowExt());
			SetViewportExt(pDC->GetViewportExt());

			pDC->DPtoLP(&m_rect);
			SetWindowOrg(m_rect.left, m_rect.top);
		} else {
			// Make a copy of the relevent parts of the current DC for printing
			m_bPrinting = pDC->m_bPrinting;
			m_hDC       = pDC->m_hDC;
			m_hAttribDC = pDC->m_hAttribDC;
		}

		// Fill background 
		if( bBg )
			BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
				m_pDC, m_rect.left, m_rect.top, SRCCOPY);
		else
			FillSolidRect(m_rect, pDC->GetBkColor());
	}
	
	~CMemDC()	
	{		
		if (m_bMemDC) {
			// Copy the offscreen bitmap onto the screen.
			m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
				this, m_rect.left, m_rect.top, SRCCOPY);			
			
			//Swap back the original bitmap.
			SelectObject(m_oldBitmap);		
		} else {
			// All we need to do is replace the DC with an illegal value,
			// this keeps us from accidently deleting the handles associated with
			// the CDC that was passed to the constructor.			
			m_hDC = m_hAttribDC = NULL;
		}	
	}
	
	// Allow usage as a pointer	
	CMemDC* operator->() 
	{
		return this;
	}	

	// Allow usage as a pointer	
	operator CMemDC*() 
	{
		return this;
	}
	   CMemDC( HDC hDC )
	   : CDC()
   {
	   CDC::FromHandle( hDC )->GetClipBox( &m_rect );
	   Build( CDC::FromHandle( hDC ) );
   }

};

#endif
           

自绘Tab控件:点击打开链接