Virtual List(虚拟列表)是LVS_OWNERDATA 样式的List Ctrl.默认的List Ctrl在插入大量的数据时会变得很慢.在我的破机器上插入不到一万行的数据要几十秒,非常令人不爽.而用Virtual List可以大大加快速度。Virtual List不拥有数据,当需要显示一行时才发消息向父窗口查询显示内容。Virtual List的使用方法与普通List Ctrl稍微有点不同。它有三个重要的消息LVN_GETDISPINFO,LVN_ODCACHEHINT和 LVN_ODFINDITEM。响应这三个消息是关键。
1.创建Virtual List 只需添加LVS_OWNERDATA风格到List Ctrl就行了。 m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_OWNERDATA);2.添加,删除和更改一行 添加:因为Virtual List不拥有数据,数据存在外部(如:数据库),所以你只需将数据添加到外部,然后m_list.SetItemCount(list_size+1)告诉Virtual List行数加一。这样Virtual List会更改滚动条的样子,以便看上去添加了一行. 删除:先在外部数据中删除行,然后m_list.SetItemCount(list_size-1)更改滚动条. 更改:直接修改在外部数据中的行.
如果这些行是正在显示的行,则需要调用m_list.RedrawItems(nFirst,nLast)
3.响应LVN_GETDISPINFO消息 当Virtual List需要显示一行数据时,它发送这个消息给父窗口询问数据内容.父窗口收到消息后从传过来参数中知道Virtual List现在显示的是第几项,第几列,内容是什么类型.然后父窗口在外部数据中找到数据内容,将它返回给Virtual List. 先添加消息映射,这里用的是反射消息,可以减少控件于父窗口之间的耦合度 BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl) ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetdispinfo) END_MESSAGE_MAP() 相应的响应函数void CMyListCtrl::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) {LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;LV_ITEM* pItem= &(pDispInfo)->item;int iItemIndx= pItem->iItem; if (pItem->mask & LVIF_TEXT){switch(pItem->iSubItem){ case 0: { memcpy(pItem->pszText,(find_index + iItemIndx)->code,8); *(pItem->pszText+8) = 0;} break; case 1:{ memcpy(pItem->pszText,(find_index + iItemIndx)-
>description,INDEX_DESLEN); *(pItem->pszText+INDEX_DESLEN) = 0; }break;}}
*pResult = 0;}
4.响应LVN_ODCACHEHINT消息 这个消息用来缓存请求项的数据.我没用到这个消息,因为我已一次将所有数据读入内存.
5.响应LVN_ODFINDITEM消息 在资源管理器中浏览文件时,让焦点list上,然后在键盘上输入文件名.资源管理器会自动找到并选中与之最相近的文件.LVN_ODFINDITEM就是实现这个功能的.当焦点在list上时,按键操作会使Virtual List发送LVN_ODFINDITEM给父窗口.父窗口找到相应项并将它选中. BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl) ON_NOTIFY_REFLECT(LVN_ODFINDITEM, OnOdfinditemList) END_MESSAGE_MAP()
void CMyListCtrl::OnOdfinditemList(NMHDR* pNMHDR, LRESULT* pResult) {// pNMHDR has information about the item we should find// In pResult we should save which item that should be selectedNMLVFINDITEM* pFindInfo = (NMLVFINDITEM*)pNMHDR;
*pResult = -1; // *pResult = -1 表明没有找到
if( (pFindInfo->lvfi.flags & LVFI_STRING) == 0 ) return;
int nlen = _tcslen(pFindInfo->lvfi.psz);
int startPos = pFindInfo->iStart;//Is startPos outside the list (happens if last item is selected)if(startPos >= m_list.GetItemCount()) startPos = 0;
int currentPos=startPos;
do{ if(memcmp((find_index + startPos ),pFindInfo->lvfi.psz,nlen) == 0) { *pResult = currentPos; break; } currentPos++; if(currentPos >= m_list.GetItemCount()) currentPos = 0;}while(currentPos != startPos); }