VC++利用GDI+实现自定义控件——BUTTON

Home / C++ MrLee 2015-8-4 3911

VC++自绘不太熟,搞了大半天终于实现了。
自己全部画控件又不能用VS的界面工具箱直接放弃,还是只能在MFC的基础上拓展吧。
使用简单的说明一下。首先要在stdafx.h中添加以下代码
#ifndef GDIPLUS
#define GDIPLUS
#include  
using namespace Gdiplus; 
#pragma comment(lib, "gdiplus.lib")
#endif 

然后在App里面添加以下代码
        //不添加此代码对IMAGE创建返回一直为NULL
	ULONG_PTR gdiplusToken;//构造函数完成
	GdiplusStartupInput gdiplusStartupInput;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	//ExitInstance
	Gdiplus::GdiplusShutdown(gdiplusToken);

添加一个变量,类型为CBUTTONEX,也可以手动附加,里面有Attach函数,关联到界面的BUTTON
#pragma once
#include "resource.h"
// CButtonEx
class CButtonEx : public CButton
{
public:
	Image *m_pImage;
	Image *m_pTemp;
	CSize m_sizeImage;
	char szTitle[MAX_PATH];
	enumButtonState m_buttonState;
	CRect m_rc;
	UINT m_frameCount;
	CButtonEx();
	virtual ~CButtonEx();
	void SetBitmap(UINT nResourceID=IDB_PNG_DEFAULT,UINT nFrameCount=4);
	virtual void OnFinalRelease();
protected:
	DECLARE_MESSAGE_MAP()
public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
	BOOL Attach(const UINT nID, CWnd* pParent);
	void OnLButtonDown(UINT nFlags, CPoint point);
	void OnLButtonUp(UINT nFlags, CPoint point);
	void OnMouseMove(UINT nFlags, CPoint point);
	void DrawContent(CDC &MemDC);
	afx_msg void OnMouseLeave();
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	virtual void PreSubclassWindow();
};

cpp文件
// ButtonEx.cpp : 实现文件
//
#include "stdafx.h"
#include "ButtonEx.h"
// CButtonEx
CButtonEx::CButtonEx()
{
	m_pImage = NULL;
	m_pTemp = NULL;
	m_buttonState = bsNormal;
}
CButtonEx::~CButtonEx()
{
	if(m_pImage != NULL)
		delete m_pImage;
	if(m_pTemp != NULL)
		delete m_pTemp;
}
void CButtonEx::OnFinalRelease()
{
	// 释放了对自动化对象的最后一个引用后,将调用
	// OnFinalRelease。基类将自动
	// 删除该对象。在调用该基类之前,请添加您的
	// 对象所需的附加清理代码。
	CWnd::OnFinalRelease();
}

BEGIN_MESSAGE_MAP(CButtonEx, CWnd)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSELEAVE()
	ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
void CButtonEx::SetBitmap(UINT nResourceID,UINT nFrameCount)
{
	ImageFromID(nResourceID,m_pImage);
	this->m_frameCount = nFrameCount;
	m_sizeImage.SetSize(m_pImage->GetWidth() / m_frameCount, m_pImage->GetHeight());
}
// CButtonEx 消息处理程序

void CButtonEx::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CWnd::OnLButtonDown(nFlags, point);
	m_buttonState = bsDown;
	Invalidate();
}

void CButtonEx::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CWnd::OnLButtonUp(nFlags, point);
	if (m_buttonState == bsDown)
	{
		if(IsWindowEnabled())
			m_buttonState = bsHover;
		else
			m_buttonState = bsDisable;
	}
}

void CButtonEx::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CWnd::OnMouseMove(nFlags, point);
	//TRACKMOUSEEVENT不加此结构体OnMouseLeave不会被调用
	TRACKMOUSEEVENT tme;
	tme.cbSize = sizeof(TRACKMOUSEEVENT);
	tme.hwndTrack = this->m_hWnd;
	tme.dwFlags = TME_LEAVE | TME_HOVER;
	_TrackMouseEvent(&tme);
	m_buttonState = bsHover;
	Invalidate();
}
void CButtonEx::OnMouseLeave()
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CButton::OnMouseLeave();
	if(IsWindowEnabled())
		m_buttonState = bsNormal;
	else
		m_buttonState = bsDisable;
	Invalidate();
}
//手动附加到窗口控件
BOOL CButtonEx::Attach(const UINT nID, CWnd* pParent)
{
	if (!SubclassDlgItem(nID, pParent))
		return FALSE;
	return TRUE;
}

void CButtonEx::DrawContent(CDC &MemDC)
{
	Graphics g(MemDC);
	g.DrawImage(m_pTemp,0,0);
	if((UINT)m_buttonState < m_frameCount)
		DrawImageFrame(g, m_pImage, m_rc, m_buttonState*(REAL)m_sizeImage.cx, 0, (REAL)m_sizeImage.cx, (REAL)m_sizeImage.cy, 3);
	COLORREF clrOld = MemDC.SetTextColor(RGB(0, 28, 48));
	CFont *pOldFont = MemDC.SelectObject(GetFont());
	MemDC.DrawText(szTitle, &m_rc, DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_WORD_ELLIPSIS);
	MemDC.SelectObject(pOldFont);
	MemDC.SetTextColor(clrOld);
}
void CButtonEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	CDC *pDc = GetDC();
	// TODO: 在此处添加消息处理程序代码
	// 不为绘图消息调用 CButton::OnPaint()
	pDc->SetBkMode(TRANSPARENT);
	CRect rcClient;
	GetClientRect(&rcClient);
	CDC MemDC;
	MemDC.CreateCompatibleDC(pDc);
	MemDC.SetBkMode(TRANSPARENT);//不设置透明文字会有背景
	CBitmap memBmp;
	memBmp.CreateCompatibleBitmap(pDc, rcClient.Width(), rcClient.Height());
	CBitmap *pOldmap =  MemDC.SelectObject(&memBmp);
	//先填充一下原先的背景
	//MemDC.BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(), pDc, 0, 0, SRCCOPY);
	DrawContent(MemDC);//直接画无背景
	//复制到原来的CDC
	pDc->BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &MemDC, 0, 0, SRCCOPY);
	//MemDC.SelectObject(pOldmap);
	//旧图应该是不存在的,直接删除
	pOldmap->DeleteObject();
	MemDC.DeleteDC();
	memBmp.DeleteObject();
	ReleaseDC(&MemDC);
	ReleaseDC(pDc);
}
BOOL CButtonEx::OnEraseBkgnd(CDC* pDC)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	//不要背景
	// TODO: 在此处添加消息处理程序代码
	// 不为绘图消息调用 CButton::OnPaint()
	CDC MemDC;
	CBitmap memBmp;
	MemDC.CreateCompatibleDC(pDC);
	memBmp.CreateCompatibleBitmap(pDC, m_rc.Width(), m_rc.Height());
	CBitmap *pOldmap =  MemDC.SelectObject(&memBmp);
	//先填充一下原先的背景
	MemDC.BitBlt(m_rc.left, m_rc.top, m_rc.Width(), m_rc.Height(), pDC, 0, 0, SRCCOPY);
	//绘制内容
	if(m_pTemp == NULL)
		BitmapToImage(&memBmp,m_pTemp);
	//旧图应该是黑的无用,直接删除
	pOldmap->DeleteObject();
	MemDC.DeleteDC();
	memBmp.DeleteObject();
	ReleaseDC(&MemDC);
	return TRUE;
}
void CButtonEx::PreSubclassWindow()
{
	// TODO: 在此添加专用代码和/或调用基类
	SetButtonStyle(GetButtonStyle()|BS_OWNERDRAW);//修改为自绘模式
	GetClientRect(&m_rc);
	GetWindowTextA(szTitle,sizeof(szTitle));//复制内容
	SetBitmap();
	CButton::PreSubclassWindow();
}

其它要绘制用到的函数
#pragma once
#include "stdafx.h"
#include "MDraw.h"
void BitmapToImage(CBitmap *pBitmap,Image* &img)
{
	//拿到位图的句柄 Bitmap是Image的子类
	HBITMAP  hBmp = (HBITMAP)pBitmap->GetSafeHandle();
	//创建一个从位图句柄的Bitmap位图
	Bitmap *bmp = new Bitmap(hBmp,NULL);
	//创建一个临时的位图
	Bitmap *temp = new Bitmap(bmp->GetWidth(),bmp->GetHeight(),PixelFormat24bppRGB);
	Graphics g(temp);//临时位图的画笔
	g.DrawImage(bmp,0,0);//将数据画到临时位图上
	img = temp;
	//最后删除位图不然会内存泄漏
	delete bmp;
}
void ImageFromID(int IDB_ID,Image* &_outImg)
{
	LPCTSTR lpType = "PNG";
	if(lpType == RT_BITMAP)
	{
		//GDI+ can not load RT_BITMAP resouce, 
		//because they are predefined resource, 
		//they don't contains the image file header.
		return;
	}
	HINSTANCE hInst = AfxGetResourceHandle();
	HRSRC hRsrc = ::FindResource(hInst,MAKEINTRESOURCE(IDB_ID),lpType);
	if (!hRsrc)
		return;
	DWORD len = SizeofResource(hInst, hRsrc);  
	BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc);  
	if (!lpRsrc)
		return;
	HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len);
	BYTE* pmem = (BYTE*)GlobalLock(m_hMem); 
	memcpy(pmem,lpRsrc,len);
	IStream* pstm;
	if (CreateStreamOnHGlobal(m_hMem,FALSE,&pstm) == S_OK)
	{
		_outImg = new Image(pstm);
		GlobalUnlock(m_hMem);
		pstm->Release();
	}
	FreeResource(lpRsrc);
}
// 画图片边框
void DrawImageFrame(Graphics &graphics, Image *pIamge, const CRect &rcControl, REAL nX, REAL nY, REAL nW, REAL nH, REAL nFrameSide/* = 3*/)
{
	// 左上角
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.left ,(REAL)rcControl.top, nFrameSide, nFrameSide),
		nX, nY, nFrameSide, nFrameSide, UnitPixel);
	// 左中边框
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.left ,(REAL)rcControl.top + nFrameSide, nFrameSide, (REAL)rcControl.Height() - 2 * nFrameSide),
		nX, nY + nFrameSide, nFrameSide, nH - 2 * nFrameSide, UnitPixel);
	// 左下角
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.left ,(REAL)rcControl.bottom - nFrameSide, nFrameSide, nFrameSide), 
		nX, nY + nH - nFrameSide, nFrameSide, nFrameSide, UnitPixel);
	// 上中边框
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.left + nFrameSide ,(REAL)rcControl.top, (REAL)rcControl.Width() - 2 * nFrameSide, nFrameSide),
		nX + nFrameSide, nY, nW - 2 * nFrameSide, nFrameSide, UnitPixel);	
	// 右上角
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.right - nFrameSide ,(REAL)rcControl.top, nFrameSide, nFrameSide), 
		nX + nW - nFrameSide, nY, nFrameSide, nFrameSide, UnitPixel);
	// 右中边框
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.right - nFrameSide ,(REAL)rcControl.top + nFrameSide, nFrameSide, (REAL)rcControl.Height() - 2 * nFrameSide), 
		nX + nW - nFrameSide, nY + nFrameSide, nFrameSide, nH - 2 * nFrameSide, UnitPixel);	
	// 右下角
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.right - nFrameSide ,(REAL)rcControl.bottom - nFrameSide, nFrameSide, nFrameSide), 
		nX + nW - nFrameSide, nY + nH - nFrameSide, nFrameSide, nFrameSide, UnitPixel);		
	// 下中边框
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.left + nFrameSide ,(REAL)rcControl.bottom - nFrameSide, (REAL)rcControl.Width() - 2 * nFrameSide, nFrameSide), 
		nX + nFrameSide, nY + nH - nFrameSide, nW - 2 * nFrameSide, nFrameSide, UnitPixel);	
	// 中间
	graphics.DrawImage(pIamge, RectF((REAL)rcControl.left + nFrameSide ,(REAL)rcControl.top + nFrameSide, (REAL)rcControl.Width() - 2 * nFrameSide, (REAL)rcControl.Height() - 2 * nFrameSide), 
		nX + nFrameSide, nY + nFrameSide, nW - 2 * nFrameSide,nH - 2 *  nFrameSide, UnitPixel);	
}

本文链接:https://www.it72.com/3972.htm

推荐阅读
最新回复 (0)
返回