惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
E
Exploit-DB.com RSS Feed
SecWiki News
SecWiki News
Forbes - Security
Forbes - Security
N
News | PayPal Newsroom
S
Security @ Cisco Blogs
Schneier on Security
Schneier on Security
V
V2EX - 技术
S
Secure Thoughts
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
S
Securelist
S
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
Security Latest
Security Latest
Recent Commits to openclaw:main
Recent Commits to openclaw:main
G
GRAHAM CLULEY
H
Hacker News: Front Page
Microsoft Azure Blog
Microsoft Azure Blog
I
Intezer
Google Online Security Blog
Google Online Security Blog
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Webroot Blog
Webroot Blog
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
P
Proofpoint News Feed
The Cloudflare Blog
I
InfoQ
L
LangChain Blog
U
Unit 42
P
Proofpoint News Feed
S
Schneier on Security
S
Security Affairs
Y
Y Combinator Blog
T
Tenable Blog
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
量子位
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
博客园 - 聂微东
D
Darknet – Hacking Tools, Hacker News & Cyber Security
GbyAI
GbyAI
AWS News Blog
AWS News Blog

博客园 - liuym

TB 编程整理 三 ICE开发初级研究 二 示例程序一 - liuym - 博客园 一 VC2008环境中ICE的配置 无法定位序数 3109 于LIBEAY32.dll - liuym 转:VC++编程之CListCtrl控件的使用 2 转:VC++编程之CListCtrl控件的使用 好用 转:删除,修改注册表中需要设置权限的项 转:软件能够修复硬盘吗?―硬盘损坏全分析 转:CWnd 对象怎么和 HWND 窗口句柄相互转化 VC 多文档窗口 子窗口最大化时切换窗口 窗口没有最大化显示的问题 转:Windows VC6编译安装Boost库 转:送给那些一心想要传送文件的朋友(TCP协议).cpp-from CSDN 转:如何去了解、熟悉一个已经开发完的项目 进行维护、二次开发或者升级 递归创建文件夹 DLL中资源和主程序资源冲突 - liuym - 博客园 转:一种巧妙的删除程序自己的方法 转:双缓冲图形刷新技术 和 WGF双缓冲绘图框架 软件保护建议(转)
转:Virtual List的使用方法
liuym · 2010-07-29 · via 博客园 - liuym

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);  

}

 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

 

 Environment: VC6 SP3, IE 4.01 

This is mainly for the list control's report view look & feel - you can use older controls, it just won't be so pretty - set in CDlgTest::OnInitDialog().

You may have noticed that the standard CListCtrl gets slow once you need to display lots of items. The time for both the fill and sort start to upset your users who have nothing but a flickering scroll bar to entertain them. Furthermore, if you've already got this data in an array, copying it into a list control is very wasteful. Microsoft's documentation says this can all be solved by virtual lists, but these are quite intimidating and most of the sample code is using classic 'Petzold' SDK style. This article gives some help on how to implement a virtual list control in an MFC project, and provides a demonstration application.

This dialog box MFC application contains both a virtual list control (IDC_LIST1) and a normal list control (IDC_LIST2) with 50,000 items so you can easily compare the differences. It also demonstrates how to:

  • make the list box display in a 'grid' style (CDlgTest::OnInitDialog)
  • use C++ classes with the qsort templates (CDlgTest::SortByCol and global CompareByLabelName / CompareByLabelAddress)
  • add icons to the list (CDlgTest::GetDispInfo)
  • override the standard sort algorithm (CDlgTest::OnColClick, CDlgTest::SortByCol)
  • change the header labels on the fly (CDlgTest::OnColClick)
  • support going to an item from a keystroke (CDlgTest::OnOdfinditem)

Virtual listing is enable by setting the 'Owner Data' property in the resources:
List Control Properties dialog box.

The dialog controls are then mapped via the DDX mechanism to data members in the dialog box class:

void CDlgTest::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);

DDX_Control(pDX, IDC_LIST2, m_List2);
DDX_Control(pDX, IDC_LIST1, m_List);

}

It's essential to supply a message handler for the LVN_GETDISPINFO message, but you can also override the LVN_COLUMNCLICK if you want to do something when the user clicks on the header button (for example sort by column content), and the LVN_ODFINDITEM if you want to respond to a normal keypress (for example move to the next item starting with the keypress).

BEGIN_MESSAGE_MAP(CDlgTest, CDialog)

ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST1, GetDispInfo)
ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnColClick)
ON_NOTIFY(LVN_ODFINDITEM, IDC_LIST1, OnOdfinditem)

END_MESSAGE_MAP()

You then need to initialise your report lists:

BOOL CDlgTest::OnInitDialog()
{
CDialog::OnInitDialog();



SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);


CString Header;
int arColWidth[]={80,100};
int iNumCols = 2;
for(int i=0; i<iNumCols; i++)
{
Header.LoadString(IDS_LISTCOL+i);
m_List.InsertColumn(i,Header,LVCFMT_LEFT,arColWidth[i]);
m_List2.InsertColumn(i,Header,LVCFMT_LEFT,arColWidth[i]);
}


m_ImageList.Create(16, 16, ILC_COLOR4, 3, 1);
m_ImageList.Add(::AfxGetApp()->LoadIcon(IDI_BP_ENABLED));
m_ImageList.Add(::AfxGetApp()->LoadIcon(IDI_BP_DISABLED));
m_ImageList.Add(::AfxGetApp()->LoadIcon(IDI_BP_NONE));
m_List.SetImageList(&m_ImageList, LVSIL_SMALL);


const int LVS_EX_LABELTIP = 0x00004000;
m_List.SetExtendedStyle(LVS_EX_FULLROWSELECT
| LVS_EX_GRIDLINES
| LVS_EX_LABELTIP);

m_List2.SetExtendedStyle(LVS_EX_FULLROWSELECT
| LVS_EX_GRIDLINES
| LVS_EX_LABELTIP);

return TRUE;
}

You'll need to add some items to your list. Here's the essential code for doing this - there's more in CDlgTest::OnAdd().
m_arLabels is the data array containing your data - note that you must maintain and clean up this array.
m_LabelCount is the number of items added to the array, this is the link between your data and the list box control via SetItemCountEx
You have to call Invalidate, otherwise the list box control won't know that the array contents have changed.


UpdateData(TRUE);

m_arLabels.SetAtGrow(...);
m_LabelCount=...;


m_List.SetItemCountEx(m_LabelCount);
m_List.Invalidate();

When the list box control content has been marked as invalid, it will try to refresh it. However, the trick about virtual lists, is that only the visible data items are requested through the LVS_GETDISPINFO message for each 'cell' it is trying to display. It is absolutely essential that you handle these messages, otherwise the control will remain stoically blank.

void CDlgTest::GetDispInfo(NMHDR* pNMHDR, LRESULT* pResult) 
{
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem= &(pDispInfo)->item;
CLabelItem rLabel = m_arLabels.ElementAt(pItem->iItem);

if (pItem->mask & LVIF_TEXT)
{

switch(pItem->iSubItem)
{
case 0:
lstrcpy(pItem->pszText, rLabel.m_strText);
break;

case 1:
sprintf(pItem->pszText, "0x%08LX", rLabel.m_Addr);
break;

default:
ASSERT(0);
break;
}
}
.
*pResult = 0;
}

There are two parameters - NMHDR* pNMHDR and LRESULT* pResult - the first is a pointer to a LV_DISPINFO structure which in turn contains a pointer to an LV_ITEM structure. It's this item information you need, as it contains the information about what the virtual list control is trying to display. You need the item's index, subitem index, and the mask - using this you can fetch the necessary information out of your data array. Note that if the request is for text, you need to copy the string to the virtual list control to be displayed. I'm not sure what the result value is used for - 0 works - it's probably reserved for something...

Anyway, at this point you should have some working virtual list. You can add more functionality by handling the other LVN_... messages, and the source code contains handlers for LVN_COLUMNCLICK and LVN_ODFINDITEM. The LVN_COLUMNCLICK is worth looking at because it shows how to optimize the sorting - as far as I can tell, the list control always uses string comparison to do the sort, which is slow if your array contains numerical data - have a look at the time difference between sorting by address on the virtual and normal list controls. I've used the qsort algorithm as it was easy to implement - which is why the string comparison on the Label column is slower than the normal list - but there's many more efficient ones out there. I'd be interested if anyone gets Dan Kozub's HybridList to work with a virtual list control.

Happy coding!

Downloads

Download source - 14 Kb
Download demo project - 8 Kb

posted on 2010-07-29 13:27  liuym  阅读(3669)  评论()    收藏  举报