1. 程式人生 > >基於MFC的CListCtrl實現虛擬列表控制元件

基於MFC的CListCtrl實現虛擬列表控制元件

1. 在建立的工程的對話方塊裡拖入一個ListCtrl控制元件,然後將控制元件的屬性View改成“Report”,“所有者資料”改成True。如下圖所示:

2. 在我們的程式中需要在ListCtrl的父視窗的類裡面響應CListCtrl的跟虛擬控制元件相關的幾個訊息事件,在父視窗的訊息巨集裡面加入下面幾個巨集:

ON_NOTIFY(LVN_ODCACHEHINT,IDC_LIST1, OnOwnerDataHint) ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST1, OnGetDispInfo) ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST1, OnCustomDraw)

關於這幾個訊息的說明:

LVN_ODCACHEHINT:該訊息在拖動ListCtrl滾動條或翻頁的時候會觸發,訊息的引數攜帶了當前頁的記錄範圍(當前頁記錄是從第i到第j個,其中i,j是記錄的行號),使用者可以在這個訊息函式裡完成一些比較耗時的載入資料操作,對未載入的資料(翻頁時新顯示的Line項)讀出來放到記憶體進行緩衝,以便到後面要回調的時候就可以快速讀記憶體。

LVN_GETDISPINFO:該訊息的響應函式用於對每行記錄的內容進行賦值,訊息帶的引數為一個LV_ITEM結構體:

--------------------- 本文來自 toshiba689 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/toshiba689/article/details/79431201?utm_source=copy

typedef struct tagLVITEMA
{
    UINT mask;
    int iItem;
    int iSubItem;
    UINT state;
    UINT stateMask;
    LPSTR pszText;
    int cchTextMax;
    int iImage;
    LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
    int iIndent;
#endif
#if (_WIN32_WINNT >= 0x0501)
    int iGroupId;
    UINT cColumns; // tile view columns
    PUINT puColumns;
#endif
#if _WIN32_WINNT >= 0x0600 // Will be unused downlevel, but sizeof(LVITEMA) must be equal to sizeof(LVITEMW)
    int* piColFmt;
    int iGroup; // readonly. only valid for owner data.
#endif
} LVITEMA, *LPLVITEMA;

上面這個結構體裡面的一些成員變數意義,iItem:Line項行號;iSubItem:列號;pszTest:該列的字串內容;iImage:圖示索引;maskd:跟項相關的風格屬性。

NM_CUSTOMDRAW:該訊息處理函式用於定義每行Line項的文字顏色和背景的顏色屬性。

3. 實現訊息處理函式,下面程式碼是我的例程中的實現方式:

//函式作用:顯示List項的內容前會觸發該訊息,緩衝還沒顯示到List控制元件中的項的資料。如果載入資料的操作比較耗時,那麼可以在使用者翻頁的時候才載入資料到記憶體。
void CVirtualListThumbnailDlg::OnOwnerDataHint(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMLVCACHEHINT   lpCacheHint = (LPNMLVCACHEHINT)pNMHDR;
	TRACE("OnOwnerDataHint From:%-4d To:%-4d\n",lpCacheHint->iFrom,lpCacheHint->iTo);
 
	//lpCacheHint->iFrom,lpCacheHint->iTo是當前新插入的List項的起始和末尾行號,表示每次翻頁或拖動滾動條時新顯示的一組Line項。
	//並不是當前頁的第一個和當前頁的最後一個記錄的行號(下面的nStartIndex和nEndIndex才表示當前頁的兩個起始和結束行號)
 
	TRACE("Cache items from %d to %d  \n", lpCacheHint->iFrom, lpCacheHint->iTo);
 
	int nLow, nHigh, nStartIndex, nEndIndex;
    int n = m_vThumb.size();
 
	//nStartIndex, nEndIndex表示當前頁的可見的Line項的範圍
	nStartIndex = m_wndList1.GetTopIndex();
	nEndIndex = nStartIndex + m_wndList1.GetCountPerPage();
	if(lpCacheHint->iFrom < nStartIndex)
		nStartIndex = lpCacheHint->iFrom;
    if(lpCacheHint->iTo > nEndIndex)
		nEndIndex = lpCacheHint->iTo;
 
	if(nEndIndex >= n)
		nEndIndex = n - 1;
 
	TRACE("Current Page Start: %d, End: %d \n", nStartIndex, nEndIndex);
 
	nLow = nHigh = -1;
 
	if(m_vCacheItems.size() > 0)
	{
		nLow = m_vCacheItems[0]->nSeqNo;
		nHigh = m_vCacheItems[m_vCacheItems.size() - 1]->nSeqNo;
 
		if(nLow <= nStartIndex && nEndIndex <= nHigh)
		{
			return;
		}
 
		if(nStartIndex >= 0 && nEndIndex < n)
		{
			m_vCacheItems.clear();
 
			for(int i=nStartIndex; i<=nEndIndex; i++)
			{
				m_vCacheItems.push_back(m_vThumb[i]);
			}
		}
		else
		{
			ASSERT(0);
		}
	}
	else
	{
		for(int i=nStartIndex; i<=nEndIndex; i++)
		{
			m_vCacheItems.push_back(m_vThumb[i]);
		}
	}
 
	*pResult = 0;
}
 
//函式作用: 在顯示一行資料時回撥,定義一行Line項的各列的字串內容,以及第一列前面的圖示的ImageIndex
void CVirtualListThumbnailDlg::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult) 
{
	LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
	int nItem = pDispInfo->item.iItem;
 
	QListItemData* pNode = GetCachedItem(nItem);
	if(pNode == NULL)
	{
		TRACE("Warning: OnGetDispInfo Item: %d requests to get source item data, it is ok but slow! \n", nItem);
		pNode = GetSourceItem(nItem);
		if(pNode == NULL)
		{
			*pResult = 0;
			TRACE("OnGetDispInfo Item: %d get item error! \n", nItem);
			return;
		}
	}
 
	//Create a pointer to the item
	LV_ITEM* pItem= &(pDispInfo)->item;
 
	//Do the list need text information?
	if (pItem->mask & LVIF_TEXT)
	{
		CString text;
 
		if(pItem->iSubItem == 0)
		{   //filename
			
			text = pNode->filename;
		}
		else if (pItem->iSubItem == 1)
		{  // filesize
			text = FormatFileSize(pNode->size);
		}
		else if (pItem->iSubItem == 2)
		{ //type
			text = pNode->filename.Right(3);
		}
 
		else if (pItem->iSubItem == 3)
		{ //  write time
			CTime time(pNode->time);
			// Format the date time string.
			text.Format( _T("%02d-%02d-%02d %02d:%02d"), 
				time.GetYear() % 100, time.GetMonth(), time.GetDay(),time.GetHour(), time.GetMinute());
		}
		//Copy the text to the LV_ITEM structure
		//Maximum number of characters is in pItem->cchTextMax
		lstrcpyn(pItem->pszText, text, pItem->cchTextMax);
	}
 
	//Do the list need image information?
	if( pItem->mask & LVIF_IMAGE) 
	{
		//Set which image to use
		if(pNode->ImageIndex==-1)
		{
			// pNode->ImageIndex = GetIconIndex(m_strCurrentDir + pNode->filename,pNode->bIsDir);
			pNode->ImageIndex = ExtToIcon(pNode->fileExt);
		}
 
		pItem->iImage = pNode->ImageIndex;
	}
	if(pItem->mask & LVIF_PARAM)
	{
		TRACE("mask has LVIF_PARAM bit set \n");
	}
 
	*pResult = 0;
}
 
//函式作用:對Line項的背景顏色,文字顏色設定,可以實現自繪的效果
void CVirtualListThumbnailDlg::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) 
{
	long lStyle = GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK;
 
	NMLVCUSTOMDRAW * lplvcd = (NMLVCUSTOMDRAW*)pNMHDR;
 
	// By default set the return value to do the default behavior.
	*pResult = 0;
 
	switch( lplvcd->nmcd.dwDrawStage )
	{
	case CDDS_PREPAINT:  
		// First stage (for the whole control)
		// Tell the control we want to receive drawing messages for drawing items.
		*pResult = CDRF_NOTIFYITEMDRAW;
		//*pResult  = CDRF_DODEFAULT;
		break;
 
	case CDDS_ITEMPREPAINT:
		{
			DWORD nItem =  lplvcd->nmcd.dwItemSpec;
			if(nItem < 0)
				break;
 
			QListItemData* pNode = GetCachedItem(nItem);
			if(pNode == NULL)
				break;
 
			//lplvcd->clrTextBk = ::ExtToColor(pNode->fileExt, FileColors, sizeof(FileColors) / sizeof(FileColors[0]));
 
			//指定文字的背景色和前景色
			lplvcd->clrTextBk = pNode->crBack;
			lplvcd->clrText = pNode->crText;
 
			*pResult = CDRF_NEWFONT| CDRF_NOTIFYPOSTPAINT;
		}
		break;
 
	case CDDS_ITEMPOSTPAINT:
		{
			int nItem =  lplvcd->nmcd.dwItemSpec;
			if(nItem < 0)
				break;
 
			QListItemData* pNode = GetCachedItem(nItem);
			if(pNode == NULL)
				break;
 
			CDC* pDC = CDC::FromHandle(lplvcd->nmcd.hdc);
			
			//For listview style: report/icon/smallicon/list
			{
				CRect rcItem;
				m_wndList1.GetItemRect( nItem, &rcItem, LVIR_ICON);
				CRect rcIcon(rcItem);
 
				int nIcon = pNode->ImageIndex;
 
			}
			*pResult = CDRF_DODEFAULT;
		}
		break;
	default: 
		*pResult = CDRF_DODEFAULT;
	}
}

--------------------- 本文來自 toshiba689 的CSDN 部落格 ,全文地址請點選:https://blog.csdn.net/toshiba689/article/details/79431201?utm_source=copy