1. 程式人生 > >【自繪ListBox之一】帶Icon的ListBox控制元件

【自繪ListBox之一】帶Icon的ListBox控制元件

參考:
CListBoxST原始碼
Demo源程式:
CIconListBox_demo
Demo程式圖片:

使用示例:
手動更改ListBox控制元件的屬性如下,因為以下特性不能通過程式碼動態修改。
m_listbox.AddString(TEXT("123"), IDI_ICON_1);
m_listbox.AddString(TEXT("456"), IDI_ICON_2);
m_listbox.AddString(TEXT("789"), IDI_ICON_1);
程式碼如下-------------------------------------------------------------------------------------------------------
CIconListBox類:
class CIconListBox : public CListBox
{
	DECLARE_DYNAMIC(CIconListBox)

	struct LBDATA
	{
		HICON hIcon;
		int   nIconHeight;
		int   nIconWidth;
	};

public:
	CIconListBox();
	virtual ~CIconListBox();

protected:
	DECLARE_MESSAGE_MAP()

public:
	virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);
	virtual void MeasureItem(LPMEASUREITEMSTRUCT /*lpMeasureItemStruct*/);

	// Interface
	int AddString(LPCTSTR lpszItem, int nIcon);
	int DeleteString(UINT nIndex);
	int InsertString(int nIndex, LPCTSTR lpszItem, int nIcon);
	void ResetContent();


private:
	void DeleteItemData(UINT nIndex);

	int m_nIconHeight;  // Icon的高度,在MeasureItem函式裡會用此確定每一項的高度
	int m_nOffset;      // 繪製Icon與Text時,與邊框的偏距
};
一 override MeasureItem
//
// 確定ListBox中每一項應繪製的高度
//
void CIconListBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	// 確定Text的高度
	ASSERT(lpMeasureItemStruct->CtlType == ODT_LISTBOX);
	LPCTSTR lpszText = (LPCTSTR) lpMeasureItemStruct->itemData;
	ASSERT(lpszText != NULL);

	CSize   sz;
	CDC*    pDC = GetDC();
	sz = pDC->GetTextExtent(lpszText);
	ReleaseDC(pDC);

	// 比較Text與Icon的高度,取大者
	int nItemHeight = 2*sz.cy > m_nIconHeight+m_nOffset*2 ? 2*sz.cy : m_nIconHeight+m_nOffset*2;
	lpMeasureItemStruct->itemHeight = nItemHeight;
}
二 override DrawItem
void CIconListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	// 準備工作
	CDC dc;
	dc.Attach(lpDrawItemStruct->hDC);
	dc.SetBkMode(TRANSPARENT);

	ASSERT(lpDrawItemStruct->CtlType == ODT_LISTBOX);
	CString cstrText;
	CListBox::GetText(lpDrawItemStruct->itemID, cstrText);
	ASSERT(!cstrText.IsEmpty());

	// 監測狀態
	BOOL bIsSelected = (lpDrawItemStruct->itemAction | ODA_SELECT) &&
		               (lpDrawItemStruct->itemState & ODS_SELECTED);
	BOOL bIsFocused = (lpDrawItemStruct->itemAction | ODA_FOCUS) &&
		              (lpDrawItemStruct->itemState & ODS_FOCUS);

	// Draw背景
	if (bIsSelected)
	{
		CBrush brushBk(::GetSysColor(COLOR_HIGHLIGHT));
		dc.FillRect(&lpDrawItemStruct->rcItem, &brushBk);
	}
	else
	{
		CBrush brushBk(dc.GetBkColor());
		dc.FillRect(&lpDrawItemStruct->rcItem, &brushBk);
	}

	// Draw icon
	LBDATA *pLbData = (LBDATA*)CListBox::GetItemDataPtr(lpDrawItemStruct->itemID); 
	if (NULL != pLbData && (LBDATA*)-1 != pLbData
		&& NULL != pLbData->hIcon)
	{
		UINT flag = DST_ICON;
		::DrawState(lpDrawItemStruct->hDC, NULL, NULL, (LPARAM)pLbData->hIcon, NULL,  
			lpDrawItemStruct->rcItem.left + m_nOffset, lpDrawItemStruct->rcItem.top + m_nOffset, 
			pLbData->nIconWidth, pLbData->nIconHeight, flag); 
	}

	// Draw text
	if (bIsSelected)
	{
		dc.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
	}
	else
	{
		dc.SetTextColor(RGB(255, 0, 0));
	}
	CRect rcText;
	rcText.left = lpDrawItemStruct->rcItem.left+ m_nOffset + pLbData->nIconWidth + m_nOffset;
	rcText.top = lpDrawItemStruct->rcItem.top + m_nOffset + m_nOffset;
	rcText.right = lpDrawItemStruct->rcItem.right - m_nOffset;
	rcText.bottom = lpDrawItemStruct->rcItem.bottom - m_nOffset;
	dc.DrawText(
		cstrText,
		&rcText,
		DT_LEFT | DT_SINGLELINE | DT_VCENTER);

	// 收尾工作
	dc.Detach();
}
三 其他函式
//
// nIcon為ICON的ID
// 注意:CListBox::AddString中會呼叫MeasureItem確定加入項的高度,所以
//      先LoadImage確定ICON的高度,並賦值給類的私有變數m_nIconHeight,
//      這樣在MeasureItem中就可以用m_nIconHeight確定Icon的高度
//
int CIconListBox::AddString(LPCTSTR lpszItem, int nIcon)
{
	LBDATA *pLbData = NULL;
	m_nIconHeight = 0;

	// 載入Icon
	HICON hIcon = (HICON)::LoadImage(::GetModuleHandle(NULL), MAKEINTRESOURCE(nIcon),   
		IMAGE_ICON, 0, 0, 0);
	if (hIcon != NULL)
	{
		pLbData = new LBDATA();

		ICONINFO ici;  
		::GetIconInfo(hIcon, &ici);  
		BITMAP bm;  
		::GetObject(ici.hbmColor, sizeof(BITMAP), &bm);
		::DeleteObject(ici.hbmColor);  
		::DeleteObject(ici.hbmMask);

		pLbData->hIcon = hIcon;
		pLbData->nIconHeight = bm.bmHeight;
		pLbData->nIconWidth = bm.bmWidth;
		m_nIconHeight = bm.bmHeight;
	}

	// Add string and lbdata
	int nIndex = CListBox::AddString(lpszItem);
	if (LB_ERR == nIndex || LB_ERRSPACE == nIndex)
	{
		if (NULL != pLbData)
		{
			delete pLbData;
			::DestroyIcon(hIcon);
		}
	}
	else
	{
		CListBox::SetItemDataPtr(nIndex, pLbData);
	}

	return nIndex;
}

int CIconListBox::InsertString(int nIndex, LPCTSTR lpszItem, int nIcon)
{
	LBDATA *pLbData = NULL;
	m_nIconHeight = 0;

	// 載入Icon
	HICON hIcon = (HICON)::LoadImage(::GetModuleHandle(NULL), MAKEINTRESOURCE(nIcon),   
		IMAGE_ICON, 0, 0, 0);
	if (hIcon != NULL)
	{
		pLbData = new LBDATA();

		ICONINFO ici;  
		::GetIconInfo(hIcon, &ici);  
		BITMAP bm;  
		::GetObject(ici.hbmColor, sizeof(BITMAP), &bm);
		::DeleteObject(ici.hbmColor);  
		::DeleteObject(ici.hbmMask);

		pLbData->hIcon = hIcon;
		pLbData->nIconHeight = bm.bmHeight;
		pLbData->nIconWidth = bm.bmWidth;
		m_nIconHeight = bm.bmHeight;
	}

	// Insert string and lbdata
	nIndex = CListBox::InsertString(nIndex, lpszItem);
	if (LB_ERR == nIndex || LB_ERRSPACE == nIndex)
	{
		if (NULL != pLbData)
		{
			::DestroyIcon(hIcon);
			delete pLbData;
		}
	}
	else
	{
		CListBox::SetItemDataPtr(nIndex, pLbData);
	}

	return nIndex;
}

int CIconListBox::DeleteString(UINT nIndex)
{
	DeleteItemData(nIndex);

	return CListBox::DeleteString(nIndex);
}

void CIconListBox::DeleteItemData(UINT nIndex)
{
	LBDATA *pLbData = (LBDATA*) CListBox::GetItemDataPtr(nIndex);
	if ((LBDATA*)-1 != pLbData && NULL != pLbData)
	{
		if (pLbData->hIcon)
			::DestroyIcon(pLbData->hIcon);
		delete pLbData;
	}
}

void CIconListBox::ResetContent()
{
	int nCount = GetCount();
	for (int i=0; i<nCount; ++i)
	{
		DeleteItemData(i);
	}

	CListBox::ResetContent();
}