-
- LVIF_TEXTThepszTextmember must be filled in.
- LVIF_IMAGETheiImagemember must be filled in.
- LVIF_INDENTTheiIndentmember must be filled in.
- LVIF_PARAMThelParammember must be filled in.
- LVIF_STATEThestatemember must be filled in.
Virtual List Controls
Visual Studio 2005
Other Versions
A virtual list control is a list view control that has theLVS_OWNERDATAstyle. This style enables the control to support an item count up to aDWORD(the default item count only extends to anint). However, the biggest advantage provided by this style is the ability to only have a subset of data items in memory at any one time. This allows the virtual list view control to lend itself for use with large databases of information, where specific methods of accessing data are already in place.
Note |
In addition to providing virtual list functionality inCListCtrl, MFC also provides the same functionality in theCListViewclass. |
There are some compatibility issues you should be aware of when developing virtual list controls. For more information, see the Compatibility Issues section of the List-View Controls topic in thePlatform SDK.
Handling the LVN_GETDISPINFO Notification
Virtual list controls maintain very little item information. Except for the item selection and focus information, all item information is managed by the owner of the control. Information is requested by the framework via aLVN_GETDISPINFOnotification message. To provide the requested information, the owner of the virtual list control (or the control itself) must handle this notification. This can easily be done using the Properties window (seeMapping Messages to Functions). The resultant code should look something like the following example (whereCMyListCtrlis the virtual list control object and the control is handling the notification):
Copy
BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetdispinfo)
END_MESSAGE_MAP()
In the handler for theLVN_GETDISPINFOnotification message, you must check to see what type of information is being requested. The possible values are:
You should then supply whatever information is requested back to the framework.
The following example (taken from the body of the notification handler for the list control object) demonstrates one possible method by supplying information for the text buffers and image of an item:
Copy
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem= &(pDispInfo)->item;
int iItemIndx= pItem->iItem;
if (pItem->mask & LVIF_TEXT) //valid text buffer?
{
switch(pItem->iSubItem){
case 0: //fill in main text
lstrcpy(pItem->pszText,
m_Items[iItemIndx].m_strItemText);
break;
case 1: //fill in sub item 1 text
lstrcpy(pItem->pszText,
m_Items[iItemIndx].m_strSubItem1Text);
break;
case 2: //fill in sub item 2 text
lstrcpy(pItem->pszText,
m_Items[iItemIndx].m_strSubItem2Text);
break;
}
}
if pItem->mask & LVIF_IMAGE) //valid image?
pItem->iImage=
m_Items[iItemIndx].m_iImageIndex;
Caching and Virtual List Controls
Because this type of list control is intended for large data sets, it is recommended that you cache requested item data to improve retrieval performance. The framework provides a cache-hinting mechanism to assist in optimizing the cache by sending anLVN_ODCACHEHINTnotification message. However, you must use a slightly different method to handle this notification. Using the Properties window, override theOnChildNotifyfunction of your list control object. In the case of this example,CMyListCtrl.
Inside the body of the handler, check for theLVN_ODCACHEHINTmessage and, if found, prepare your cache.
The following example (taken from the body of theOnChildNotifyfunction) performs this check and calls thePrepCachemember function of theCMyListCtrlclass.
Copy
NMLVCACHEHINT* pcachehint=NULL;
if (message == WM_NOTIFY)
{
NMHDR* phdr = (NMHDR*)lParam;
switch(phdr->code)
{
case LVN_ODCACHEHINT:
pcachehint= (NMLVCACHEHINT*) phdr;
// Load the cache with the recommended range.
PrepCache(pcachehint->iFrom, pcachehint->iTo);
break;
default:
return CListCtrl::OnChildNotify(message, wParam, lParam, pLResult);
}
return FALSE;
}
else
return CListCtrl::OnChildNotify(message, wParam, lParam, pLResult);
Notice that the notification is passed on to the base class (CListCtrl) if the message type is notLVN_ODCACHEHINT. For more information on preparing and maintaining a cache, see the Cache Management section of the List-View Controls topic in thePlatform SDK.
Finding Specific Items
TheLVN_ODFINDITEMnotification message is sent by the virtual list control when a particular list control item needs to be found. The notification message is sent when the list view control receives quick key access or when it receives anLVM_FINDITEMmessage. Search information is sent in the form of anLVFINDINFOstructure, which is a member of theNMLVFINDITEMstructure. Handle this message by overriding theOnChildNotifyfunction of your list control object and inside the body of the handler, check for theLVN_ODFINDITEMmessage. If found, perform the appropriate action.
You should be prepared to search for an item that matches the information given by the list view control. You should return the index of the item if successful, or -1 if no matching item is found.
See Also
Reference
Concepts
源文档 <http://msdn.microsoft.com/en-us/library/ye4z8x58(v=vs.80).aspx>
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的插入操作做了些什么呢?
源文档 <http://blog.vckbase.com/zgf/archive/2005/11/25/15271.aspx>
使用listview 操作大量的数据的时候,使用virtual list 是必要的。使用方法比较简单。
1、创建控件的时候制定LVS_OWNERDATA
2、添加 notification LVN_GETDISPINFO 的响应函数 ,在消息映射处加入 ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetDispInfo)
3、 一个OnGetDispInfo 的例子:
void CVirtualListView::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
TCHAR szValue[MAX_PATH];
try
{
long index = pDispInfo->item.iItem;
long subItem = pDispInfo->item.iSubItem;
sprintf(szValue, "[%d].[%d]" , index , subItem );
strcpy (pDispInfo->item.pszText, szValue );
} catch (... ) {
}
*pResult = 0;
}
4. 通过 CListCtrl 的 SetItemCount 函数来设置Item 的数目。或者直接向控件发送LVM_SETITEMCOUNT 消息来完成设置,其实是一样的。 :)
5、
两个通知是虚列表特有的。第一个,LVN_ODFINDITEM,在控件要查找一个特定的项时被发送;例如当输入焦点在控件上时,用户输入了一些东西。这个通知的lParam 是一个指向NMLVFINDITEM结构的指针。这个结构包含一个NMHDR结构 (hdr)、一个查找起始项的索引和一个LVFINDINFO(以前的LV_FINDINFO)结构(lvfi)。这个信息提供给你,使得你可以查找指定的列表项。你应该返回找到的列表项的索引,或者-1(如果没有找到)。
第二个通知是LVN_ODCACHEHINT。 这个通知在列表将要显示某个范围内的列表项时发送。这个通知的lParam 是一个指向NMLVCACHEHINT结构的指针。这个结构包含一个NMHDR结构 (hdr)、一个指出范围的开始,的整数,和一个指出范围的结束的整数。处理这个通知,你需要载入指定的列表项到本地缓存,如果你正在维护一个的话。控件为未缓存的列表项仍可能发送LVN_GET-DISPINFO,所以你的应用程序必须考虑这种情况
6.响应LVN_ODCACHEHINT消息
这个消息用来缓存请求项的数据.我没用到这个消息,因为我已一次将所有数据读入内存.
7.响应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);
源文档 <http://www.cnblogs.com/vingo888/archive/2006/01/10/314720.html>