CListView虛擬列表


首先說下虛擬列表出現的原因:

數據量比較小的時候,對於CListView控件可以直接使用InsertItem並配合SetItemText函數來插入並修改數據。這樣操作很直接。

但是,如果數據量比較大了,比如1w個數據,那么,根據插入的數據種類,長度,以及計算機性能,估計時間在10s中到1分鍾不等。如果你的用戶在使用的這樣的程序時肯定不會開心,初始化的時候插入則需要登上10s+后才能使用,如果外加一個線程來並行插入,倒也是一個方法(這個事情我做過),不過,很浪費CPU時間,以及內存。

如果是10w個,甚至100w個數據呢?那么至少會線性的增加時間了~

但是回頭想一想,一個列表,在你的計算機顯示器上,最多也就能看到50-80個。我的筆記本分辨率是1366*768的就按50個來算,768/50 約等於15吧,一行用15個像素寬度表示,已經有點小勉強了~~~

所以這個時候有個事情就非常明顯了:在大數據量的時候,根本沒必要在初始化時把全部的數據都插入到CListCtrl控件中。

要注意,這個問題是我在下自習回寢室的路上想明白的~~~

然后我就想了,既然這樣的話,如果自己動手做虛擬列表,也不是不能做。基本上需要准備好以下的東西:

1 右面的瀏覽滑塊,就是那個scroll,這個需要處理好,根據不同的位置,動態加載不同地方的數據。

2 鼠標的滾輪消息,上下移動,也要做好。

3 窗口的最大數據量,以及文字顯示之類的東西,都要做。

基本上就是意味着需要自己做一個控件,虛擬列表控件。這個確實是可行的。然后呢~我在百度查資料的時候,意外的看到了虛擬列表這個東西~~~

那時我才知道,原來microsoft的大神們已經想到了這個問題,而且在CListCtrl中已經整合好了。

在CListCtrl中使用虛擬列表

我之前的那篇關於CListCtrl控件使用方法的文章中說過,對於和控件綁定了的CListCtrl對象,主要需要做的工作,就是設置風格,並且插入列。

不過對於虛擬列表,與常規的列表相比,並沒有什么風格上的不同,所以風格還是依照自己的需求進行設置就可以了。

列的控制,按照前面那篇文章上來就好了~這里不是重點。

然后呢,很需要做的一點就是,設置最大條目的數量。這里要注意的是,所謂的最大條目的數量,就是和你的數據庫的數據量。這兩者一定是要匹配的。

展示一小段實例代碼:

 1 else
 2     {
 3         CFileInfo cfi;
 4         while(m_MyDataBase.ReadString(path))
 5         {
 6             cfi.csFileRoot = path;
 7             cfi.csFileName = path.Right(path.GetLength() - path.ReverseFind('\\') - 1);
 8             cfi.csFilePath = path.Left(path.ReverseFind('\\'));
 9             m_arrayFileInfo.Add(cfi);
10             nFileNum++;
11         }
12         m_MyDataBase.Close();
13     }
14     
15     m_LCTable.SetItemCountEx(nFileNum, LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
16     nInitialFlag = 1;

最上面的代碼是數據初始化的部分,存儲到CArray模板中。也算是個最簡單的數據庫吧。

初始化過后,我的數據就放到了容器中,上面的nFileNum變量就是跟蹤容量的數據,不過,其實也不需要,畢竟CArray是提供數據總量查詢的。

然后接下來調用我們的明星函數:SetItemCountEx

這個函數,第一個參數毫無疑問就是設置數量上限的,第二個參數又是一個什么風格設置,看看MSDN怎么說:

    • LVSICF_NOINVALIDATEALL The list view control will not repaint unless affected items are currently in view. This is the default value.

    • LVSICF_NOSCROLL The list view control will not change the scroll position when the item count changes. 

大意我就不翻譯了,現在有個人正在和我聊天,翻譯了太費時間。這兩個的特性可以自己試一試。

然后在這算是第一步完成,主要還是SetItemCountEx函數。

 

接下來,需要做的事進行消息響應。

要知道,windows程序的運行是需要消息來推動的。當滑塊對拖動或者鼠標滾動的時候,都會有消息產生。

所以,CListCtrl也是采用了這種機制,消息響應的方式去填充虛擬列表。

我在這里只介紹我使用過的一個消息,就是LVN_GETDISPINFO

最上面的那個消息響應就是了。

然后我們看響應函數的代碼:

 1 void CMyRisingDlg::OnLvnGetdispinfoList2(NMHDR *pNMHDR, LRESULT *pResult)
 2 {
 3     NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
 4     // TODO: Add your control notification handler code here
 5     LV_DISPINFO * pLPD = (LV_DISPINFO *)pDispInfo;
 6     LV_ITEM* pItem= &(pDispInfo)->item;
 7     HICON hIconTmp;
 8     int nItem = pItem->iItem;
 9     if (pItem->mask & LVIF_TEXT) //valid text buffer?
10     {
11        switch(pItem->iSubItem)
12        {
13           case 0: //fill in main text首列添加圖像的工作肯定也要在這里完成
14              _tcscpy(pItem->pszText, m_arrayFileInfo[nItem].csFileName);
15              //ExtractIconEx(m_arrayFileInfo[nItem].csFileRoot, 0, NULL, &hIconTmp, 1);
16              //m_imagelist.Add(hIconTmp);
17              //Add Icon into the list
18              pItem->iImage = 0;
19              //m_imagelist.Remove(0);
20              break;
21           case 1: //fill in sub item 1 text
22              _tcscpy(pItem->pszText, m_arrayFileInfo[nItem].csFilePath);
23              break;
24           case 2: //fill in sub item 2 text
25              _tcscpy(pItem->pszText, m_arrayFileInfo[nItem].FileTime);
26              break;
27        }
28     }
29 
30     *pResult = 0;
31 }

首先能看參數,其中一個是NMHDR的指針,這個類型,我還真的不是很清楚。

不過清楚的可以看到,經過兩次類型轉換,我們會得到一個LV_ITEM的指針。在這簡單的說一下,前面的那段類型轉換的代碼,是函數自動生成的時候就已經改出來的。

接着,在這里我們只要根據LV_ITEM中的消息進行相應來添加相應的數據就可以了。

所以接下來主要需要看的就是switch中的case選擇。

在上面代碼中的switchcase結構中,傳給switch的參數就是一個條目中的子序號。根據這個子序號,去添加對應位置的信息。

然后這里還有非常重要的一點,就是在上面的這段代碼:

 8     int nItem = pItem->iItem;

這段代碼就是用來獲取當前位置對應數據的目錄號。只有根據這個目錄號,你才能找到在你的數據中應該添加的數據的位置。這個在我的代碼中你應該也能注意到的。

最主要的兩個數據在這里基本就介紹完了,具體的代碼也貼在上面。這樣就能進行最簡單的虛擬列表操作啦。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM