MFC自绘控件系列-复合按钮PNG贴图

文章详细介绍了如何在MFC中创建一个具有安卓风格的复合按钮,包括处理闪烁问题、实现不同状态下的图像切换、自定义绘制文本和图标等。通过设置不同的图像资源,实现了按钮的正常、悬停、按下和禁用状态。同时,提供了设置文字颜色、检查框、下拉选项等功能。
摘要由CSDN通过智能技术生成

      

      

       在MFC实现上面的安卓风格的复合按钮是有难度的,尝试了好几种方法,显示闪烁问题都是绕不过去的坎。幸得高手指点,完美解决了此问题,分享给大家。

        

头文件:

#pragma once

class CItemView : public CButton
{
	DECLARE_DYNAMIC(CItemView)

	CItemView();
	virtual ~CItemView();

protected:
	DECLARE_MESSAGE_MAP()
	// ReSharper disable once IdentifierTypo
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	afx_msg void OnMouseHover(UINT nFlags, CPoint point);
	afx_msg void OnMouseLeave();
	void PreSubclassWindow() override;
	void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) override;

//接口
public:

	void SetImages(UINT nNormalId, UINT nHoverId, UINT nPressId, UINT nDisableId = 0, LPCTSTR lpszResourceType = _T("PNG"));//button
	void SetIcon(UINT nId);
	void SetTextParam(COLORREF cr);

	void SetCheckImage(UINT nCheckId, UINT nNormalId); // check box

	void SetValueString(CString &cs,int rightIconID = -1, int redDotID = -1); // text
	void SetValueText(CString& str);
	void SetValueRedDot(BOOL on);

	void SetSelectValue(CString strInit, int itemCount, UINT boxID); // select box
	void SetSelectText(CString str);
	int GetSelectStaus();

	static void LoadImageFromResource(CImage& img, UINT nId, LPCTSTR lpszResourceType = "PNG");

	virtual BOOL Create(LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);

private:
	void outputText(CDC *cdc, int nDpi);
	
protected:
	void ReleaseImages();
	BOOL m_isInit;
	enum State {
		Normal = 0,
		Hover,
		Press,
		Disable,
	};
	CImage		m_images[4];
	State		m_state = Normal;
	BOOL        m_bMouseTrack;
	CImage		m_icon;
	CImage		m_checkImage[2];//0: normal, 1: check
	CRect       m_itemValueRect;
	COLORREF m_textColor;

	CString     m_valueString;
	CImage m_rightIcon; // 右图标
	BOOL m_isRedDot; // 红点
	CImage m_imgRedDot; // 红点图片

	CString m_selectText;
	UINT m_selectBoxID;
	int m_selectorCount;
	int m_selectStaus; // 0-无 1-鼠标点击

public:
	bool m_isCheck;
};

.cpp文件

// ImageButton.cpp: 实现文件
//

#include "pch.h"
#include "ItemView.h"
#include <VersionHelpers.h>

// CImageButton

IMPLEMENT_DYNAMIC(CItemView, CButton)

CItemView::CItemView()
	: m_images()
	, m_bMouseTrack(TRUE)
{
	m_textColor = RGB(10, 10, 10);

	m_selectorCount = 0;
	m_selectStaus = 0;
	m_isCheck = false;
	m_isRedDot = false;
	m_isInit = false;
}

CItemView::~CItemView()
{
	ReleaseImages();
}

BEGIN_MESSAGE_MAP(CItemView, CButton)
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEHOVER()
	ON_WM_MOUSELEAVE()
	ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()

static BOOL createFont(int size, CFont &font)
{
	font.CreateFont(size,                                    //   字体的高度   
		0,                                          //   字体的宽度  
		0,                                          //  nEscapement 
		0,                                          //  nOrientation   
		FW_NORMAL,                                  //   nWeight   
		FALSE,                                      //   bItalic   
		FALSE,                                      //   bUnderline   
		0,                                                   //   cStrikeOut   
		ANSI_CHARSET,                             //   nCharSet   
		OUT_DEFAULT_PRECIS,                 //   nOutPrecision   
		CLIP_DEFAULT_PRECIS,               //   nClipPrecision   
		DEFAULT_QUALITY,                       //   nQuality   
		DEFAULT_PITCH | FF_SWISS,     //   nPitchAndFamily     
		_T("微软雅黑"));

	return true;
}

// CImageButton 消息处理程序

BOOL CItemView::OnEraseBkgnd(CDC* pDC)
{
	// 处理背景擦除消息, 实现完全透明
	return TRUE; // CButton::OnEraseBkgnd(pDC);
}


void CItemView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// 鼠标按下状态
	m_state = Press;

	if (m_itemValueRect.PtInRect(point)) {
		if (!m_checkImage[0].IsNull()) {
			//TRACE("on item value Rect.\n");
			m_isCheck = !m_isCheck;
		} else if (m_selectorCount > 0) {
			m_selectStaus = 1;
			//TRACE("on selector mouse down.\n");
		}
	}
	Invalidate();
	CButton::OnLButtonDown(nFlags, point);
}


void CItemView::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_state = Hover;
	Invalidate();

	CButton::OnLButtonUp(nFlags, point);
}


void CItemView::OnMouseMove(UINT nFlags, CPoint point)
{
	// 追踪鼠标
	if (m_bMouseTrack)
	{
		TRACKMOUSEEVENT eventTrack;
		eventTrack.cbSize = sizeof(eventTrack);
		eventTrack.dwFlags = TME_LEAVE | TME_HOVER;
		eventTrack.hwndTrack = m_hWnd;					// 指定要追踪的窗口
		eventTrack.dwHoverTime = 1;						// 鼠标在按钮上停留超过1ms, 才认为状态为HOVER
		_TrackMouseEvent(&eventTrack);					// 开启Windows的WM_MOUSELEAVE, WM_MOUSEHOVER事件支持
		m_bMouseTrack = FALSE;							// 若已经追踪, 则停止追踪
	}

	CButton::OnMouseMove(nFlags, point);
}


void CItemView::OnMouseHover(UINT nFlags, CPoint point)
{
	m_state = Hover;
	Invalidate();

	CButton::OnMouseHover(nFlags, point);
}


void CItemView::OnMouseLeave()
{
	m_state = Normal;

	m_bMouseTrack = TRUE;

	Invalidate();

	CButton::OnMouseLeave();
}


void CItemView::PreSubclassWindow()
{
	// 设置自绘模式
	ModifyStyle(0, BS_OWNERDRAW);

	CButton::PreSubclassWindow();
}

#define PAD_RIGHT 15
#define PAD_LEFT 10

void CItemView::outputText(CDC *pDC, int nDpi)
{
	CFont font;
	CRect clientRC;
	TEXTMETRICA metrics;
	int offsetx, offsety;

	GetClientRect(&clientRC);
	const auto nItemWidth = clientRC.Width();
	const auto nItemHeight = clientRC.Height();

	UINT sizeFont = (UINT)(nItemHeight * 0.55);
	sizeFont = sizeFont & (-2);
	createFont(sizeFont, font);
	pDC->SelectObject(&font);
	pDC->SetBkMode(TRANSPARENT);
	pDC->GetTextMetrics(&metrics);
	offsetx = dp(50, nDpi);

	{//text out item name.
		CString cs;
		GetWindowText(cs);
		pDC->SetTextColor(m_textColor);
		pDC->TextOut(offsetx, (nItemHeight - metrics.tmHeight) / 2, cs);
	}

	if (!m_valueString.IsEmpty()) {
		CSize strSize;
		strSize = pDC->GetTextExtent(m_valueString);

		int offsetCheck = 0;
		if (!m_checkImage[0].IsNull()) {
			offsetCheck = dp(m_checkImage[0].GetWidth()+5, nDpi);
		}

		int offsetIcon = 0;
		if (!m_rightIcon.IsNull()) {
			offsetIcon = dp(m_rightIcon.GetWidth()+5, nDpi);
		}

		int offsetRedDot = 0;
		if (m_isRedDot && !m_imgRedDot.IsNull()) {
			offsetRedDot = dp(m_imgRedDot.GetWidth() + 5, nDpi);
		}

		offsetx = clientRC.Width() - strSize.cx * 1.05 - offsetCheck - offsetIcon - offsetRedDot - PAD_RIGHT;

		pDC->SetTextColor(RGB(10, 10, 10));
		pDC->TextOut(offsetx, (nItemHeight - metrics.tmHeight) / 2, m_valueString);
	}

	if (m_selectorCount > 0) {
		sizeFont = (UINT)(nItemHeight * 0.46);
		sizeFont = sizeFont & (-2);

		CFont font2;
		createFont(sizeFont, font2);
		pDC->SelectObject(&font2);
		pDC->GetTextMetrics(&metrics);

		CSize strSize;
		strSize = pDC->GetTextExtent(m_selectText);
		offsetx = m_itemValueRect.left + (m_itemValueRect.Width() * 0.8 - strSize.cx)/2;

		pDC->SetTextColor(RGB(10, 10, 10));
		pDC->TextOut(offsetx, (nItemHeight - metrics.tmHeight) / 2, m_selectText);
	}
}

void CItemView::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	CDC memDc;
	CBitmap bmp;
	auto pDC = CDC::FromHandle(lpDrawItemStruct->hDC);

	if (lpDrawItemStruct->itemState & ODS_DISABLED) {
		m_state = Disable;
	}

	CRect clientRC;
	GetClientRect(&clientRC);
	const auto nItemWidth = clientRC.Width();
	const auto nItemHeight = clientRC.Height();

	memDc.CreateCompatibleDC(pDC);
	bmp.CreateCompatibleBitmap(pDC, nItemWidth, nItemHeight);
	memDc.SelectObject(&bmp);

	int nDpi = 96;
	auto&& image = m_images[m_state];
	if (!image.IsNull()) {
		image.Draw(memDc, 0, 0, nItemWidth, nItemHeight, 0, 0, image.GetWidth(), image.GetHeight());
		nDpi = MulDiv(nItemHeight,96,image.GetHeight());
	}

	if (!m_icon.IsNull()) {//icon
		int w = dp(m_icon.GetWidth(), nDpi);
		int h = dp(m_icon.GetHeight(), nDpi);
		int x = dp(PAD_LEFT, nDpi);
		int y = (clientRC.Height() - h) / 2;
		m_icon.Draw(memDc, x, y, w, h, 
			0, 0, m_icon.GetWidth(), m_icon.GetHeight());
	}

	if (!m_checkImage[0].IsNull()) { //value
		auto&& img = m_checkImage[m_isCheck];
		int realW = dp(img.GetWidth(), nDpi);
		int realH = dp(img.GetHeight(), nDpi);

		m_itemValueRect.left = nItemWidth - dp((img.GetWidth() + PAD_RIGHT), nDpi);
		m_itemValueRect.top = (nItemHeight - realH)/2;
		m_itemValueRect.right = m_itemValueRect.left + realW;
		m_itemValueRect.bottom = m_itemValueRect.top + realH;
		img.Draw(memDc, m_itemValueRect.left, m_itemValueRect.top, realW, realH, 0, 0, img.GetWidth(), img.GetHeight());
	}

	if (!m_valueString.IsEmpty()) {
		if (!m_rightIcon.IsNull()) {
			int x = nItemWidth - dp(m_rightIcon.GetWidth() + PAD_RIGHT, nDpi);
			int y = (nItemHeight - dp(m_rightIcon.GetHeight(), nDpi)) / 2;
			int realW = dp(m_rightIcon.GetWidth(), nDpi);
			int realH = dp(m_rightIcon.GetHeight(), nDpi);

			m_rightIcon.Draw(memDc, x, y, realW, realH, 0, 0, m_rightIcon.GetWidth(), m_rightIcon.GetHeight());
		}

		if (m_isRedDot && !m_imgRedDot.IsNull()) {
			int offsetIcon = 0;
			if (!m_rightIcon.IsNull()) {
				offsetIcon = m_rightIcon.GetWidth();
			}

			int x = nItemWidth - dp(m_imgRedDot.GetWidth() + offsetIcon + PAD_RIGHT, nDpi);
			int y = (nItemHeight - dp(m_imgRedDot.GetHeight(), nDpi)) / 2;
			int realW = dp(m_imgRedDot.GetWidth(), nDpi);
			int realH = dp(m_imgRedDot.GetHeight(), nDpi);

			m_imgRedDot.Draw(memDc, x, y, realW, realH, 0, 0, m_imgRedDot.GetWidth(), m_imgRedDot.GetHeight());
		}
	}

	if (m_selectorCount > 0) {
		CImage img;
		LoadImageFromResource(img, m_selectBoxID);
		int x = nItemWidth - dp(img.GetWidth() + PAD_RIGHT, nDpi);
		int y = (nItemHeight - dp(img.GetHeight(), nDpi)) /2;
		int realW = dp(img.GetWidth(), nDpi);
		int realH = dp(img.GetHeight(), nDpi);

		m_itemValueRect.left = x;
		m_itemValueRect.top = y;
		m_itemValueRect.right = m_itemValueRect.left + realW;
		m_itemValueRect.bottom = m_itemValueRect.top + realH;
		img.Draw(memDc, x, y, realW, realH, 0, 0, img.GetWidth(), img.GetHeight());
	}

	BLENDFUNCTION bf;
	bf.AlphaFormat = AC_SRC_ALPHA;
	bf.BlendFlags = 0;
	bf.BlendOp = AC_SRC_OVER;
	bf.SourceConstantAlpha = 255;
	pDC->AlphaBlend(0, 0, nItemWidth, nItemHeight, &memDc, 0, 0, nItemWidth, nItemHeight, bf);

	outputText(pDC, nDpi);

	bmp.DeleteObject();
	memDc.DeleteDC();
}
void CItemView::SetImages(UINT nNormalId, UINT nHoverId, UINT nPressId, UINT nDisableId, LPCTSTR lpszResourceType)
{
	ReleaseImages();
	LoadImageFromResource(m_images[Normal], nNormalId, lpszResourceType);
	LoadImageFromResource(m_images[Hover], nHoverId, lpszResourceType);
	LoadImageFromResource(m_images[Press], nPressId, lpszResourceType);
	nDisableId = nDisableId == 0 ? nNormalId : nDisableId;
	LoadImageFromResource(m_images[Disable], nDisableId, lpszResourceType);
}

void CItemView::SetIcon(UINT nId)
{
	LoadImageFromResource(m_icon, nId);
}

void CItemView::SetTextParam(COLORREF cr)
{
	m_textColor = cr;
}

void CItemView::SetCheckImage(UINT nCheckId, UINT nNormalId)
{
	LoadImageFromResource(m_checkImage[0], nNormalId);
	LoadImageFromResource(m_checkImage[1], nCheckId);
}

void CItemView::SetValueString(CString &cs, int rightIconID, int redDotID) 
{
	m_valueString = cs;
	if (rightIconID != -1) {
		LoadImageFromResource(m_rightIcon, rightIconID);
	}
	if (redDotID != -1) {
		LoadImageFromResource(m_imgRedDot, redDotID);
	}
}

void CItemView::SetValueText(CString& str)
{
	m_valueString = str;
	if (m_isInit) {
		Invalidate();
	}
}

void CItemView::SetValueRedDot(BOOL on)
{
	m_isRedDot = on;
	if (m_isInit) {
		Invalidate();
	}
}

void CItemView::SetSelectValue(CString strInit, int itemCount, UINT boxID)
{
	m_selectorCount = itemCount;
	m_selectBoxID = boxID;
	m_selectText = strInit;
}

void CItemView::SetSelectText(CString str)
{
	m_selectText = str;
	Invalidate();
}

int CItemView::GetSelectStaus()
{
	int status = m_selectStaus;
	m_selectStaus = 0;
	return status;
}

void CItemView::LoadImageFromResource(CImage& img, UINT nId, LPCTSTR lpszResourceType)
{
	const static auto hInstance = AfxGetInstanceHandle();

	const auto hResource = ::FindResource(hInstance, MAKEINTRESOURCE(nId), lpszResourceType);
	if (hResource == nullptr) {
		return;
	}

	const auto hResData = LoadResource(hInstance, hResource);
	if (hResData == nullptr) {
		return;
	}

	const auto lpResource = LockResource(hResData);
	if (lpResource == nullptr) {
		return;
	}

	const auto dwResourceSize = SizeofResource(hInstance, hResource);

	const auto hMem = GlobalAlloc(GMEM_FIXED, dwResourceSize);
	if (hMem == nullptr) {
		return;
	}

	const auto lpMem = static_cast<LPBYTE>(GlobalLock(hMem));
	if (lpMem == nullptr) {
		GlobalFree(hMem);
		return;
	}

	memcpy(lpMem, lpResource, dwResourceSize);

	const auto pStream = SHCreateMemStream(lpMem, dwResourceSize);
	if (pStream != nullptr) {
		if (img.Load(pStream) == S_OK && _tcsicmp(lpszResourceType, _T("PNG")) == 0 && img.GetBPP() == 32) {
			// 预处理PNG的Alpha通道
			for (auto i = 0; i < img.GetWidth(); i++) {
				for (auto j = 0; j < img.GetHeight(); j++) {
					const auto pColor = reinterpret_cast<LPBYTE>(img.GetPixelAddress(i, j));
					pColor[0] = pColor[0] * pColor[3] / 255;
					pColor[1] = pColor[1] * pColor[3] / 255;
					pColor[2] = pColor[2] * pColor[3] / 255;
				}
			}
		}
	}

	GlobalUnlock(lpMem);
	GlobalFree(hMem);
}

void CItemView::ReleaseImages()
{
	for (auto&& image : m_images)
	{
		image.Destroy();
	}
}

BOOL CItemView::Create(LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
	if (m_images[0].IsNull()) {
		TRACE("CItemView::Create() fail\n");
		return FALSE;
	}

	BOOL ret = CButton::Create(lpszCaption, dwStyle, rect, pParentWnd, nID);
	m_isInit = true;

	return ret;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值