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 selected
 NMLVFINDITEM* 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);  
}

     使用Virtual List后的效果令我很满意,但我还是不明白普通list ctrl为什么插入速度这么慢?普通list ctrl的插入操作做了些什么呢?