MFC自绘控件系列(GDI+)-列表框PNG贴图

       常规的GDI自绘控件仅支持BMP图片,不支持png图片。png图片体积小,支持透明色,可以做圆角界面。自绘控件想要支持PNG图片,需要在MFC工程引入GDI+。需要注意的是MFC工程默认是不支持GDI+,需要手工引入相关的库与头文件,具体方法见我的博客相关的文章。

      自绘列表框的关键是重载OnPaint(),绘制背景框图,重载DrawItem()函数,绘画条目文本,切换条目的选中状态。

      第二个关键点是重载PreSubclassWindow()函数,在其中定义按键的风格,其中必须定义的风格是LBS_OWNERDRAWVARIABLE、LBS_NOINTEGRALHEIGHT、WS_EX_TRANSPARENT,需要去除WS_BORDER风格,其它的风格酌情选用。

     第三个关键点是重载MeasureItem()、CompareItem()函数,定义条目的高度与排序规则。

      如果想实现控件叠加的效果,必须拦截WM_ERASEBKGND消息,因为CButton默认擦除背景的操作是复制对话框的背景色,而不是下面的控件的皮肤。
 

头文件:

#pragma once
#include <afxwin.h>

class CPngListBox : public CListBox
{
public:
	CPngListBox();
	~CPngListBox();

	BOOL Init(UINT resID, int itemHeigh, int edgeSpace);
	void SetColor(COLORREF normalColor, COLORREF selectColor, COLORREF edgeColor);

private:
	COLORREF m_NormalColor;
	COLORREF m_selectColor;
	COLORREF m_EdgeColor;
	int m_EdgeSpace;
	int m_ItemHeigh;

	Image* m_imgNormal;

	Image* ImageFromRes(UINT uImgID, LPCTSTR lpType);
	void ImageFree();

	BOOL ShowImage(CDC* pDC, Image* pImage);

//重载
public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDIT);
	virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMIT);
	virtual void PreSubclassWindow();
	virtual int CompareItem(LPCOMPAREITEMSTRUCT lpCIT);
	DECLARE_MESSAGE_MAP()
	afx_msg void OnPaint();
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
};

.cpp文件

#include "pch.h"
#include "CPngListBox.h"

CPngListBox::CPngListBox()
{
	m_NormalColor = RGB(250, 250, 250); //白色
	m_selectColor = RGB(126, 206, 244); //设置为默认天蓝色
	m_EdgeColor = GetSysColor(COLOR_BTNFACE); //边框颜色为按钮灰
	m_EdgeSpace = 5; //上下边距默认5
	m_ItemHeigh = 37;
}

CPngListBox::~CPngListBox()
{
	ImageFree();
}

BOOL CPngListBox::Init(UINT resID,int itemHeigh,int edgeSpace)
{
	m_imgNormal = ImageFromRes(resID, "PNG");

	if (m_imgNormal == NULL) {
		TRACE("ImageFromResource fail[%d]\n", GetLastError());
		ImageFree();
		return FALSE;
	}

	m_ItemHeigh = itemHeigh;
	m_EdgeSpace = edgeSpace;

	SetWindowPos(NULL, 0, 0, m_imgNormal->GetWidth(), m_imgNormal->GetHeight(), SWP_NOACTIVATE | SWP_NOMOVE);

	return TRUE;
}

void CPngListBox::SetColor(COLORREF normalColor, COLORREF selectColor, COLORREF edgeColor)
{
	m_NormalColor = normalColor;
	m_selectColor = selectColor;
	m_EdgeColor = edgeColor;
}

void CPngListBox::DrawItem(LPDRAWITEMSTRUCT lpDIT)
{
	// TODO:  添加您的代码以绘制指定项

	ASSERT(lpDIT->CtlType == ODT_LISTBOX);
	/*LPCTSTR lpszText = (LPCTSTR)lpDIT->itemData;
	if (lpszText == NULL) {
		TRACE("CPngListBox::DrawItem() text=NULL\n");
		return;
	}*/

	CString strText;
	GetText(lpDIT->itemID, strText);
	if (strText.GetLength() <= 0) {
		TRACE("CPngListBox::DrawItem() text=NULL\n");
		return;
	}

	CDC dc;
	dc.Attach(lpDIT->hDC);

	COLORREF crOldTextColor = dc.GetTextColor();
	COLORREF crOldBkColor = dc.GetBkColor();

	CRgn rgn1;
	RECT r = lpDIT->rcItem;
	RECT tr = { r.left + m_EdgeSpace, r.top + m_EdgeSpace, r.right - m_EdgeSpace, r.bottom};
	rgn1.CreateRectRgnIndirect(&tr);

	//窗口被选中 或者为活动
	if ((lpDIT->itemAction | ODA_SELECT) && (lpDIT->itemState & ODS_SELECTED))
	{
		dc.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));//字体颜色白色
		dc.SetBkColor(m_selectColor);//::GetSysColor(COLOR_HIGHLIGHT)

		CBrush brush1(m_selectColor);
		dc.FillRgn(&rgn1, &brush1);
		brush1.DeleteObject();
	}
	else//窗口未被选中
	{
		//内嵌窗体颜色绘制
		CBrush brush1(m_NormalColor);
		dc.FillRgn(&rgn1, &brush1);

		//内嵌窗体边框绘制
		CBrush brush2(m_EdgeColor);
		dc.FrameRgn(&rgn1, &brush2, 2, 2);

		brush1.DeleteObject();
		brush2.DeleteObject();
	}

	// 选中某一条目时使其边框高亮 
	/*if ((lpDIT->itemAction | ODA_FOCUS) && (lpDIT->itemState & ODS_FOCUS))
	{

		CBrush br(RGB(0, 0, 128));
		dc.FrameRect(&lpDIT->rcItem, &br);
	}*/

	lpDIT->rcItem.left += 5;

	TEXTMETRICA textInfo;
	dc.GetTextMetrics(&textInfo);
	textInfo.tmHeight = 16;
	dc.GetTextMetrics(&textInfo);

	// Draw the text.  
	dc.DrawText(strText, &tr, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
 
	dc.SetTextColor(crOldTextColor);
	dc.SetBkColor(crOldBkColor);
	dc.Detach();
	rgn1.DeleteObject();
}


void CPngListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMIT)
{
	// TODO:  添加您的代码以确定指定项的大小

	ASSERT(lpMIT->CtlType == ODT_LISTBOX);
	//LPCTSTR lpszText = (LPCTSTR)lpMIT->itemData;
	//ASSERT(lpszText != NULL);

	CString strText;
	GetText(lpMIT->itemID, strText);
	if (strText.GetLength() <= 0) {
		TRACE("CPngListBox::DrawItem() text=NULL\n");
		return;
	}

	CSize sz;
	CDC* pDC = GetDC();

	sz = pDC->GetTextExtent(strText);

	ReleaseDC(pDC);

	int height = 2 * sz.cy;
	lpMIT->itemHeight = (m_ItemHeigh > 0) ? m_ItemHeigh : height;
}


void CPngListBox::PreSubclassWindow()
{
	// TODO: 在此添加专用代码和/或调用基类

	ModifyStyle(0, LBS_OWNERDRAWVARIABLE | LBS_NOINTEGRALHEIGHT | WS_EX_TRANSPARENT);
	ModifyStyle(LBS_SORT | WS_BORDER | WS_VSCROLL, 0);

	CListBox::PreSubclassWindow();
}


int CPngListBox::CompareItem(LPCOMPAREITEMSTRUCT lpCIT)
{

	// TODO:  添加您的代码以确定指定项的排序顺序
	// 返回 -1 表示项 1 排在项 2 之前
	// 返回 0 表示项 1 和项 2 顺序相同
	// 返回 1 表示项 1 排在项 2 之后

	return -1;
}

BOOL CPngListBox::ShowImage(CDC* pDC, Image* pImage)
{
	bool bSuc = false;

	if (pImage != NULL)
	{
		CRect rcButton;
		rcButton = CRect(0, 0, pImage->GetWidth(), pImage->GetHeight());

		Graphics graph(pDC->GetSafeHdc());
		graph.DrawImage(pImage, 0, 0, rcButton.left, rcButton.top, rcButton.Width(), rcButton.Height(), UnitPixel);
		graph.ReleaseHDC(pDC->GetSafeHdc());
		bSuc = true;
	}

	return bSuc;
}

Image* CPngListBox::ImageFromRes(UINT uImgID, LPCTSTR lpType)
{
	HINSTANCE hInstance = AfxGetResourceHandle();
	HRSRC hResInfo = ::FindResource(hInstance, MAKEINTRESOURCE(uImgID), lpType);
	if (hResInfo == NULL)
		return NULL; //fail
	DWORD dwSize;
	dwSize = SizeofResource(hInstance, hResInfo); //get resource size(bytes) 
	HGLOBAL hResData;
	hResData = ::LoadResource(hInstance, hResInfo);
	if (hResData == NULL)
		return NULL; //fail
	HGLOBAL hMem;
	hMem = ::GlobalAlloc(GMEM_MOVEABLE, dwSize);
	if (hMem == NULL) {
		::FreeResource(hResData);
		return NULL;
	}
	LPVOID lpResData, lpMem;
	lpResData = ::LockResource(hResData);
	lpMem = ::GlobalLock(hMem);
	::CopyMemory(lpMem, lpResData, dwSize); //copy memory
	::GlobalUnlock(hMem);
	::FreeResource(hResData); //free memory

	IStream* pStream;
	HRESULT hr;
	hr = ::CreateStreamOnHGlobal(hMem, TRUE, &pStream);//create stream object
	Image* pImage = NULL;
	if (SUCCEEDED(hr)) {
		pImage = Image::FromStream(pStream);//get GDI+ pointer
		pStream->Release();
	}
	::GlobalFree(hMem);
	return pImage;
}

void CPngListBox::ImageFree()
{
	if (m_imgNormal){
		delete m_imgNormal;
		m_imgNormal = NULL;
	}
}

BEGIN_MESSAGE_MAP(CPngListBox, CListBox)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
END_MESSAGE_MAP()


void CPngListBox::OnPaint()
{
	CPaintDC dc(this); // device context for painting

	ShowImage(&dc, m_imgNormal);

	int curSel = GetCurSel();
	int count = GetCount();
	SetCurSel(-1);
	for (int i = 0; i < count; i++) {
		SetCurSel(i);
	}

	SetCurSel(curSel);
}


BOOL CPngListBox::OnEraseBkgnd(CDC* pDC)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	//return CListBox::OnEraseBkgnd(pDC);
	return TRUE;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值