VC 虚拟列表.docx

上传人:b****1 文档编号:10685677 上传时间:2023-05-27 格式:DOCX 页数:27 大小:27.67KB
下载 相关 举报
VC 虚拟列表.docx_第1页
第1页 / 共27页
VC 虚拟列表.docx_第2页
第2页 / 共27页
VC 虚拟列表.docx_第3页
第3页 / 共27页
VC 虚拟列表.docx_第4页
第4页 / 共27页
VC 虚拟列表.docx_第5页
第5页 / 共27页
VC 虚拟列表.docx_第6页
第6页 / 共27页
VC 虚拟列表.docx_第7页
第7页 / 共27页
VC 虚拟列表.docx_第8页
第8页 / 共27页
VC 虚拟列表.docx_第9页
第9页 / 共27页
VC 虚拟列表.docx_第10页
第10页 / 共27页
VC 虚拟列表.docx_第11页
第11页 / 共27页
VC 虚拟列表.docx_第12页
第12页 / 共27页
VC 虚拟列表.docx_第13页
第13页 / 共27页
VC 虚拟列表.docx_第14页
第14页 / 共27页
VC 虚拟列表.docx_第15页
第15页 / 共27页
VC 虚拟列表.docx_第16页
第16页 / 共27页
VC 虚拟列表.docx_第17页
第17页 / 共27页
VC 虚拟列表.docx_第18页
第18页 / 共27页
VC 虚拟列表.docx_第19页
第19页 / 共27页
VC 虚拟列表.docx_第20页
第20页 / 共27页
亲,该文档总共27页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

VC 虚拟列表.docx

《VC 虚拟列表.docx》由会员分享,可在线阅读,更多相关《VC 虚拟列表.docx(27页珍藏版)》请在冰点文库上搜索。

VC 虚拟列表.docx

VC虚拟列表

[VC/MFC]虚拟列表控件

2008-10-0719:

12一、什么是虚拟列表控件

虚拟列表控件是指带有LVS_OWNERDATA风格的列表控件。

二、为什么使用虚拟列表控件

我们知道,通常使用列表控件CListCtrl,需要调用InsertItem把要显示的数据插入列表中,之后我们就不必关心数据在哪里了,这是因为控件自己开辟了内存空间来保存这些数据。

现在假设我们要显示一个数据库,里面的信息量很大,有几十万条记录。

通常有两种方法解决这个问题:

1是仅仅在ListCtrl中插入少量的数据,比如100个,然后通过[上一页][下一页]两个按钮进行控制,某一时刻显示的只是从xxx到xxx+100之间的记录。

2是把所有数据全部插入到ListCtrl中,然后让用户通过滚动来查看数据。

无疑,很多用户喜欢采用第二种方式,特别是对于已经排序的数据,用户只需用键盘输入某行的开头字符,就可以快速定位到某一行。

但是,如果这样做,InsertItem插入数据的过程将是很漫长的,而且用户会看到ListCtrl刷新速度也很慢,而且所有数据都位于内存中消耗了大量的内存,当数据多达上万以后几乎是不能忍受的。

为此,mfc特别提供了虚拟列表的支持。

一个虚拟列表看起来和普通的ListCtrl一样,但是不用通过InsertItem来插入数据,它仅仅知道自己应该显示多少数据。

但是它如何知道要显示什么数据呢?

秘密就在于当列表控件需要显示某个数据的时候,它向父窗口要。

假设这个列表控件包含100个元素,第10到20个元素(行)是可见的。

当列表控件重画的时候,它首先请求父窗口给它第10个元素的数据,父窗口收到请求以后,把数据信息填充到列表提供的一个结构中,列表就可以用来显示了,显示第10个数据后,列表会继续请求下一个数据。

在虚拟的样式下,ListCtrl可以支持多达DWORD个数据项。

(缺省的listctrl控件最多支持int个数据项)。

但是,虚拟列表的最大优点不在于此,而是它仅仅需要在内存中保持极少量的数据,从而加快了显示的速度。

所以,在使用列表控件显示一个很大的数据库的情况下,采用虚拟列表最好不过了。

不仅CListCtrl提供虚拟列表的功能,MFC的CListView类也有同样的功能。

三、虚拟列表控件的消息

虚拟列表总共发送三个消息给父窗口:

当它需要数据的时候,它发送LVN_GETDISPINFO消息。

这是最重要的消息。

当用户试图查找某个元素的时候,它发送LVN_ODFINDITEM消息;还有一个消息是LVN_ODCACHEHINT,用来缓冲数据,基本上很少用到这个消息。

虚拟列表控件使用起来非常简单。

它总共只有三个相关的消息,如果你直接使用CListCtrl,应该在对话框中响应这三个消息。

如果你使用CListCtrl派生类,可以在派生类中响应这三个消息的反射消息。

这三个消息分别是:

(1)LVN_GETDISPINFO控件请求某个数据

(2)LVN_ODFINDITEM查找某个数据

(3)LVN_ODCACHEHINT缓冲某一部分数据

我们必须响应的消息是

(1),多数情况下要响应

(2),极少数的情况下需要响应(3)

四、如何使用虚拟列表控件

1、首先要创建控件,创建一个虚拟列表和创建一个正常的CListCtrl差不多。

先在资源编辑器里面添加一个listcontrol资源。

然后选中"Ownerdata"属性,然后给它捆绑一个CListCtrl变量。

添加列,添加imagelist等都和使用正常的listctrl一样。

2、给虚拟列表添加元素。

假设m_list是这个列表的控制变量。

通常的情况下这样添加数据:

m_list.InsertItem(0,_T("Helloworld"));

但是对于虚拟列表,不能这么做。

只需告诉列表有多少个数据:

//总共显示100行

m_list.SetItemCount(100);

3、处理它的通知消息。

五、如何响应虚拟列表的消息

1、处理LVN_GETDISPINFO通知消息

当虚拟列表控件需要某个数据的时候,它给父窗口发送一个LVN_GETDISPINFO通知消息,表示请求某个数据。

因此列表的所有者窗口(或者它自己)必须处理这个消息。

例如派生类的情况(CMyListCtrl是一个虚拟列表类对象):

//这里处理的是反射消息

BEGIN_MESSAGE_MAP(CMyListCtrl,CListCtrl)

//{{AFX_MSG_MAP(CMyListCtrl)

ON_NOTIFY_REFLECT(LVN_GETDISPINFO,OnGetdispinfo)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

在LVN_GETDISPINFO的处理函数中,必须首先检查列表请求的是什么数据,可能的值包括:

(1)LVIF_TEXT必须填充pszText

(2)LVIF_IMAGE必须填充iImage

(3)LVIF_INDENT必须填充iIndent

(4)LVIF_PARAM必须填充lParam

(5)LVIF_STATE必须填充state

根据它的请求,填充所需的数据即可。

//=================例子代码=====================================

下面的给出一个例子,填充的是列表所需的某个数据项的文字以及图像信息:

LV_DISPINFO*pDispInfo=(LV_DISPINFO*)pNMHDR;

LV_ITEM*pItem=&(pDispInfo)->item;

intiItemIndx=pItem->iItem;

if(pItem->mask&LVIF_TEXT)//字符串缓冲区有效

{

switch(pItem->iSubItem){

case0:

//填充数据项的名字

lstrcpy(pItem->pszText,m_Items[iItemIndx].m_strItemText);

break;

case1:

//填充子项1

lstrcpy(pItem->pszText,m_Items[iItemIndx].m_strSubItem1Text);

break;

case2:

//填充子项2

lstrcpy(pItem->pszText,m_Items[iItemIndx].m_strSubItem2Text);

break;

}

}

/*注意,多数情况下要使用lstrcpyn,因为最多复制字符的个数由pItem->cchTextMax给出:

lstrcpyn(pItem->pszText,text,pItem->cchTextMax);

*/

if(pItem->mask&LVIF_IMAGE)//是否请求图像

pItem->iImage=m_Items[iItemIndx].m_iImageIndex;

甚至连某行数据是否选中(当有checkbox的情况下)的信息也需要由用户自己来维护,例如:

//是否显示该行的选择信息?

if(IsCheckBoxesVisible())//自定义函数

{

pItem->mask|=LVIF_STATE;

pItem->stateMask=LVIS_STATEIMAGEMASK;

if(m_database[itemid].m_checked)

{

pItem->state=INDEXTOSTATEIMAGEMASK

(2);

}

else

{

pItem->state=INDEXTOSTATEIMAGEMASK

(1);

}

}

2、处理LVN_ODFINDITEM消息

在资源管理器里面,定位到某个文件夹,会显示很多文件,如果按下键盘的‘A’,则资源管理器会自动找到名字以'A'打头的文件夹或者文件,并选择该文件。

继续按A,如果还有其它名字以'A'打头的文件,则下一个文件被选中。

如果输入"AB",则'AB'打头的文件被选中。

这就是列表控件的自动查找功能。

当虚拟列表收到一个LVM_FINDITEM消息,它也会发送这个消息通知父窗口查找目标元素。

要搜索的信息通过LVFINDINFO结构给出。

它是NMLVFINDITEM结构的一个成员。

当找到要搜索的数据后,应该把该数据的索引(行号)返回,如果没有找到,则返回-1。

以对话框为例,响应函数大致如下:

//=================例子代码=====================================

voidCVirtualListDlg:

:

OnOdfinditemList(NMHDR*pNMHDR,LRESULT*pResult)

{

//pNMHDR里面是要查找的元素的信息

//要选中的目标元素的行号最后要保存在pResult中,这是关键!

NMLVFINDITEM*pFindInfo=(NMLVFINDITEM*)pNMHDR;

/*pFindInfo->iStart是查找的起始位置,一直到最后,然后从头开始,如果没有找到合适的,最终停留在iStart*/

*pResult=-1;

//是否按照文字查找?

if((pFindInfo->lvfi.flags&LVFI_STRING)==0)

{

return;

}

//这是我们要找的字符串

CStringsearchstr=pFindInfo->lvfi.psz;

intstartPos=pFindInfo->iStart;//保存起始位置

//判断是否最后一行

if(startPos>=m_list.GetItemCount())

startPos=0;

intcurrentPos=startPos;

//开始查找

do

{

if(_tcsnicmp(m_database[currentPos].m_name,

searchstr,searchstr.GetLength())==0)

{

//选中这个元素,停止查找

*pResult=currentPos;

break;

}

currentPos++;

//从头开始

if(currentPos>=m_list.GetItemCount())

currentPos=0;

}while(currentPos!

=startPos);

}

显然,如果数据很多,必须实现一个快速查找的方法。

关于pFindInfo->lvfi里面的信息的详细说明可以参考MSDN。

3、处理LVN_ODCACHEHINT消息。

假如我们从数据库或者其它地方读取数据的速度比较慢,则可以利用这个消息,批量读取一些数据,然后根据请求,逐个提供给虚拟列表。

LVN_ODCACHEHINT消息的用途就是给程序一个缓冲数据的机会。

以提高程序的性能。

//=================例子代码=====================================

使用ClassWizard重载OnChildNotify函数,检查是否LVN_ODCACHEHINT消息,然后准备缓冲数据:

NMLVCACHEHINT*pcachehint=NULL;

NMHDR*phdr=(NMHDR*)lParam;

if(phdr->code==LVN_ODCACHEHINT)

{

pcachehint=(NMLVCACHEHINT*)phdr;

//自定义函数,准备指定范围的数据到缓冲区

PrepCache(pcachehint->iFrom,pcachehint->iTo);

}

else...

注意,如果消息不是LVN_ODCACHEHINT,则要传递给基类进行处理。

五、如何修改ListCtrl显示的数据。

由于是程序自己维护数据,所以只需修改数据库中的数据,然后调用CListCtrl:

:

RedrawItems函数进行重画即可。

六、数据的选择状态和选择框

CListCtrl可以显示checkbox选择框。

有些情况下是很有用的。

对于正常的listctrl,用户可以用鼠标来修改某个元素的选择状态,但是对于虚拟列表就不行了。

必须自己处理一些消息,然后自己保存数据的选中状态:

voidCVirtualListDlg:

:

ToggleCheckBox(intitem)

{

m_database[item].m_checked=!

m_database[item].m_checked;

m_list.RedrawItems(item,item);

}

处理LVN_KEYDOWN消息,添加对空格键的响应,用于切换选择状态:

voidCVirtualListDlg:

:

OnKeydownList(NMHDR*pNMHDR,LRESULT*pResult)

{

LV_KEYDOWN*pLVKeyDown=(LV_KEYDOWN*)pNMHDR;

if(pLVKeyDown->wVKey==VK_SPACE)

{

intitem=m_list.GetSelectionMark();

if(item!

=-1)

ToggleCheckBox(item);

}

*pResult=0;

}

然后处理NM_CLICK消息:

voidCVirtualListDlg:

:

OnClickList(NMHDR*pNMHDR,LRESULT*pResult)

{

NMLISTVIEW*pNMListView=(NM_LISTVIEW*)pNMHDR;

LVHITTESTINFOhitinfo;

hitinfo.pt=pNMListView->ptAction;

intitem=m_list.HitTest(&hitinfo);

if(item!

=-1)

{

//看看鼠标是否单击在checkbox上面了?

if((hitinfo.flags&LVHT_ONITEMSTATEICON)!

=0)

{

ToggleCheckBox(item);

}

}

*pResult=0;

}

七、备注:

1、虚拟列表无法进行排序。

2、虚表的一个优点是容易保持和数据库的同步。

修改数据库中的数据,然后重画list十分容易而且高效。

3、虚表的另一个优点是可以根据需要产生数据。

比如在某一列加上行号。

 

CListCtrl大数据显示

分类:

MFC

2009-03-1212:

211004人阅读评论(0)收藏举报

CListCtrl是个很方便的东西,但是当数据大到一个程度(比如说10万条数据),显示速度就会非常的慢。

解决办法就是用虚拟列表。

CListCtrl显示数据的原理是将需显示的所有数据拷贝在它内部的一块空间里,然后显示出来。

一但数据量过大,拷贝的时间就会延长,显示速度当然也就非常慢了。

而虚拟列表则不需要将显示数据拷贝到内部空间,它的做法是当需要显示某个数据时,才将数据拷入内部空间。

看上去好像和普通CListCtrl的做法相同,实则大不一样。

因为列表一屏的数据,最多也就几百行。

也就是说虚拟列表每次最多也就只需要拷贝几百行的数据,对现在计算机的速度而言,速度的延迟是完全可以忽略的。

好了,原理说到这里。

接下来该说做怎么做了。

[cpp:

showcolumns]viewplain

copy

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150

classCVirtaulListCtrl:

publicCListCtrl

{

DECLARE_DYNAMIC(CVirtaulListCtrl)

public:

CVirtaulListCtrl();

virtual~CVirtaulListCtrl();

public:

//导入数据

voidImportData(CStringArray*pDataArray)

{

m_pDataArray=pDataArray;

SetItemCount(m_pDataArray->GetSize());

}

protected:

CStringArray*m_pDataArray;

protected:

DECLARE_MESSAGE_MAP()

public:

afx_msgvoidOnLvnGetdispinfo(NMHDR*pNMHDR,LRESULT*pResult)

{

LV_DISPINFO*pDispInfo=(LV_DISPINFO*)pNMHDR;

LV_ITEM*pItem=&(pDispInfo)->item;

intiItemIndex=pItem->iItem;//行号

intcol=pItem->iSubItem;//列号

if(pItem->mask&LVIF_TEXT)

{

if(col==0)

{

//序号

charnumberStr[10];

itoa(iItemIndex+1,numberStr,10);

lstrcpyn(pItem->pszText,numberStr,pItem->cchTextMax);

}

elseif(col==1)

{

lstrcpyn(pItem->pszText,m_pDataArray->GetAt(iItemIndex),pItem->cchTextMax);

}

}

}

};

上面一共只有两个函数。

一个是ImportData,作用是导入需要显示的数据。

里面只有两条语句,第一条为获取数据指针,第二条为设置列表长度(一共有多少条数据)。

这个是自定义函数,可以自定义函数名和导入的数据结构。

第二个函数是OnLvnGetdispinfo。

这个函数是事件函数,当列表中的一个单元格需显示的时候调用。

使用时,还需要将列表控件的“所有者数据”属性设为“true”。

 

MFC总结之CListCtrl用法及技巧

(一)

本文根据本人在项目中的应用,来谈谈CListCtrl的部分用法及技巧。

当初学习时,查了很多资料,零零碎碎的作了些记录,现在主要是来做个总结,方便以后查阅。

主要包括以下十三点内容:

基本操作、获取选中行的行号、复选框操作、动态设置选中行的字体颜色、设置选中行的背景颜色、禁止拖动表头、让第一列居中显示、设置行高与字体、虚拟列表技术、点击表头时进行归类、向上与向下移动、动态调整大小问题、避免闪烁问题。

分为两篇来进行总结。

本篇重点总结:

基本操作、获取选中行的行号、复选框操作、动态设置选中行的字体颜色、设置选中行的背景颜色

1、基本操作

分别从下面四点来介绍CListCtrl的基本操作:

①设置列表视图显示方式

Ⅰ.CListCtrl有四种样式:

LVS_ICON、LVS_SMALLICON、LVS_LIST、LSV_REPORT,可通过控件属性来设置。

本文所述均为LSV_REPORT属性。

Ⅱ.扩展样式:

常用的扩展样式有三种:

LVS_EX_FULLROWSELECT、LVS_EX_GRIDLINES、LVS_EX_CHECKBOXES,分别对应作用选中某行时使正行高亮、设置网格线、item前生成Ckeckbox控件。

使用SetExtendedStyle(style)函数设置扩展样式,使用GetExtendedStyle()函数获取样式,如:

m_listInfo.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);

Ⅲ.使用CListView时,需要在PreCreateWindow()函数中添加cs.style|=LVS_REPORT;

来将其设置为LVS_REPORT风格,否则插入无效。

还用另一种方法来设置风格,即在OnInitialUpate()中获取CListCtrl控制权,然后修改风格,如下所示:

CListCtrl&theCtrl=GetListCtrl();

theCtrl.ModifyStyle(0,LVS_REPORT);

②插入操作

先插入列:

intInsertColumn(intnCol,LPCTSTRlpszColumnHeading,intnFormat,intnWidth,intnSubItem)

插入列时,可指明列号、列名称、列名称显示样式,列宽等信息。

对于列号为0的那一列,始终是靠左显示,后面会有修改使其剧中显示的方法,其他列通过设置nFormat属性可以居中显示。

插入行:

intInsertItem(intnItem,LPCTSTRlpszItem)

直接插入一行,nItem指明行号,lpszItem指明该行第0列的信息。

设置信息:

BOOLSetItemText(intnItem,intnSubItem,LPCTSTRlpszText)

设置第nItem行nSubItem列的信息(nItem:

0,1,2,3……;nSubItem:

1,2,3……)

③删除操作

有三个操作函数:

BOOLDeleteAllItems()-------删除所有的行

BOOLDeleteItem(nItem)--------删除某一行

BOOLDeleteColumn(nCol)-----删除某一列

④获取/设置属性函数

有很多函数了,就不一一介绍了。

常用的有

intGetItemCount()--------获取已插入信息的行数

BOOLSetItemState(intiLink,UINTstate,UINTstateMask)---------设置行状态,如高亮显示等

等等

2、获取选中行的行号

获取选中行的行号,然后对该行进行相关处理,这点在编程中用的非常多。

当鼠标单击item时,控件向父窗口发送NM_CLICK消息,其响应函数为OnNMClickXXXX(NMHDR*pNMHDR,LRESULT*pResult),在该函数下来编写代码获取鼠标点击的行号。

有两种方法来获取行号:

第一种是使用GetFirstSelectedItemPosition和GetNextSelectedItem配合来获取;第二种是先获取鼠标位置信息,然后调用HitTest函数来找出行号。

示例分别如下:

第一种方法,该示例截自MSD

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2