Virtual
ListView via API
The ListView control is ideal for displaying
large amounts of data. Unfortunately, when the number of tiems gets
extremely large, the ListView becomes unbearably slow. It's hard
to imagine that a ListView would be slow, as Explorer itself is a fancy
ListView. With the release of IE3 (comctrl32 version 4.70 MS introduced
the LVS_OWNERDATA style -- the virtual list view control intended for raw
speed.
At this point, Windows does not support
dynamically changing the ListView to virtual mode. What this means
is, you can't use an existing TListView, then call GetWindowLong()/SetWindowLong().
This imposes additionaly work since you have to create and manage everything
dynamically. So, the first task is to use the CreateWindowEx() function
to create a ListView -- not a TListView, but a WC_LISTVIEW (window control
ListView). Then, tell the ListView how many items it is to contain
by using the ListView_SetItemCountEx() macro. This is not implemented
in some versions, so I defined it below. Next, you'll need to attach
an ImageList to the ListView using the ListView_SetImageList() macro.
Finally, you need to map the WM_NOTIFY message and examine it for the LVN_GETDISPINFO
code. The basic idea is this: The ListView knows how many items it
has (specified by the ListView_SetItemCount() call), and whenever it needs
to display an item, it sends its Parent (your Form), a WM_NOTIFY message.
Within this message is the information it's requesting (i.g., item caption,
image index, etc.). In turn, you respond by telling the ListView
what to display, in your case, strings from your StringList and an index
from your ImageList. Windows does the rest. All the memory
management for the items is handled by you (in your StringList).
That's why it's called a virtual ListView -- it's nothing more than a container
for you to display items in. The ListView itself has no idea what
it contains, it just knows how many, and what style to display
the data in.
KEYWORDS: LVS_OWNERDATA, CreateWindowEx
|
//in
header...
//define
the ListView constants...
#define
LVS_OWNERDATA
0x1000
#define
LVSICF_NOINVALIDATEALL 0x00000001
#define
LVSICF_NOSCROLL 0x00000002
#define
LVM_SETITEMCOUNT (LVM_FIRST +
47)
#define
ListView_SetItemCountEx(hwndLV, cItems, dwFlags) \
SNDMSG((hwndLV), LVM_SETITEMCOUNT, (WPARAM)cItems, (LPARAM)dwFlags)
//functions
for creation and management...
HWND
HListView;
HWND
CreateVirtListView(DWORD style, int x, int y, int
width, int height);
void
__fastcall WMNotify(TMessage &Msg);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_NOTIFY,
TMessage, WMNotify)
END_MESSAGE_MAP(TForm)
|
//---------------------------------------------------------------------------
const
int num_items = 25000;
__fastcall
TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// create the virtual ListView
HListView = CreateVirtListView(LVS_LIST, 10,
10, 200, 250);
if (HListView)
{
// specify the number of items
ListView_SetItemCountEx(HListView, num_items, LVSICF_NOINVALIDATEALL);
// bind an ImageList to your ListView
ListView_SetImageList(HListView, ImageList1->Handle, LVSIL_SMALL);
}
}
void
__fastcall TForm1::WMNotify(TMessage &Msg)
{
LPNMHDR lpnmhdr = (NMHDR *)Msg.LParam;
if (lpnmhdr->code == LVN_GETDISPINFO)
{
LV_DISPINFO *lplvdi = (LV_DISPINFO *) Msg.LParam;
// index of item to display
int index = lplvdi->item.iItem;
if (lplvdi->item.mask & LVIF_IMAGE)
{
// decide what image to display, for example
// I made all even items have image index zero
// and all odd items have image index of one.
if (index % 2 == 0) lplvdi->item.iImage
= 0;
else lplvdi->item.iImage = 1;
}
if (lplvdi->item.mask & LVIF_TEXT)
{
// fill in the pszText member with data from your StringList
lstrcpy(lplvdi->item.pszText, SL->Strings[index].c_str());
}
Msg.Result = false;
}
else TForm::Dispatch(&Msg);
}
//
function to create the virtual list view
HWND
TForm1::CreateVirtListView(DWORD style, int x, int y,
int width, int height)
{
DWORD dwStyle = WS_TABSTOP | WS_CHILD | WS_BORDER | WS_VISIBLE |
LVS_AUTOARRANGE | style | LVS_OWNERDATA;
HWND HListView = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL,
dwStyle, 0, 0, 0, 0, Handle, (HMENU)0,
HInstance, NULL);
if (!HListView) return NULL;
::MoveWindow(HListView, x, y, width, height, true);
return HListView;
}
|
|