vbpslogo2.gif (19593 bytes)
Brad's VB-32 Programs & Samples

Last update: 11/14/99 (VBDirectoryLV, LVItemTips)

Home

Extending the functionality of the VB ListView controls

Overview...

The "ListView" control in both Comctl32.ocx and Mscomctl.ocx is simply a wrapper that superclasses (encapsulates) the real "listview" common control in Comctl32.dll. This allows the real listview common control messages and definitions to be used to override almost every aspect of the VB ListView control.

The listview common control messages and definitions can be found in a C header file named Commctrl.h. This file used to have a friendly URL on the MS site, but doesn't anymore for some reason. It appears that the only alternatives for obtaining this file are to either grab a copy of Visual Studio, or download either of the fairly huge INetSDK or PlatformSDK at http://msdn.microsoft.com/developer/sdk/

But, instead of going to all of the trouble to find the darned download that contains Commctrl.h (wherever it is), and then having to convert the C definitions to VB, well, you can get the whole shebang right here. The follow zip contains *every* listview definition and macro converted to VB, through Comctl32.dll v4.71 (IE4,  excluding the listview's CustomDraw definitions). Most listview examples that will eventually appear below will use only the necessary subset of these definitions, keeping each example as clear and simple as possible.

Download lvdefs_471.zip (9kb)
Last update: 08/30/99

LVItemDrag: How to dynamically drag selected ListItems in the VB ListView.

Even though one or more ListItems can be selected in the ListView whose MultiSelect property is set to True (whose LVS_SINGLESEL window style is not set), unlike familiar Explorer functionality, only the focused ListItem moves when dragging the selection. This demo solves this shortcoming, but not without a little work.

When the left or right mouse button is depressed and moved over ListItem, typically four pixels in any direction, the real listview sends its parent window a LVN_BEGINDRAG or LVN_BEGINRDRAG notification message, respectively. For left button dragging, when the VB ListView receives a LVN_BEGINDRAG, it begins repositioning the ListItem under the cursor. The ListView does not respond to LVN_BEGINRDRAG, and provides no right button item dragging facility. Once either notification has been processed and dragging has begun, the ListView stops raising its MouseMove event, until the respective button is released. So, in order to provide multiple ListItem repositioning with the left mouse button, right button ListItem repositioning, and continue the receive the MouseMove event, the ListView's parent window (typically a Form) must be subclassed, where both notifications can be detected, and eaten (not passed to the CallWindowProc API function).

Also, when in large or small icon view, the view area inside the ListView's client window in which its icons reside can grow to be much larger than the size of the ListView's client window. If an ListItem is dragged to the edge of the ListView client window, the view becomes enlarged and is shifted in the opposite direction. Because item positions (ListItem Left and Top properties) are coordinates relative to the view's 0-0 coordinates, and not the upper-left corner of the ListView client window, this amount of view shifting must be considered when positioning ListItems using the ListView client cursor position. The demo makes heavy use of the LVM_GETORIGIN message, which returns the pixel coordinates of the ListView client window's upper-left corner relative to the shifted view's 0-0 coordinates.

Not only does the demo show how to move all selected ListItems with the mouse during a dragging operation, but also solves the annoying behavior where the ListView will scroll wildly when the selected ListItem is dragged near the ListView client window edge.

Download lvitemdrag.zip (9kb)
Last update: 08/30/99

LVDragImage: How to create a drag image of selected ListView ListItems.

As does the previous demo, this demo also shows how to move selected ListItems in the ListView, but takes a completely different, and quite a bit more complex approach, by showing how to create Explorer like drag images for selected ListView ListItems.

If a single item is dragged in Explorer's listview, a faded image of that item is created and "attached" to the cursor. If multiple items are selected and dragged, on < Win2K OSs (Win95, Win98, NT4), the drag image is just an outline of the selected items' icons and labels. On Win2K, the drag image is comprised of the selected items' actual icons and labels, that are not only faded (just like a single item's drag image), but are highlighted as well. This demo shows how to create all three drag image styles: outlined, faded, and faded and highlighted; how to move the drag image with the cursor, and finally how to reposition all selected items to where they are dropped inside the ListView.

To create a faded (and not highlighted) drag image of a single item, the listview's LVM_CREATEDRAGIMAGE message is normally used. But since the listview has no means to create a drag image for multiple items, as well as no message for creating an outlined, or faded and highlighted drag image, the demo shows how to actually construct and draw all three drag image styles in a memory DC for one or more items. Though not the most efficient of methods, the demo uses the services of the imagelist drag APIs to move the constructed image around the screen with the cursor.

Even if the amount of code and overhead required to provide drag image functionality may not be practical, it is still interesting to see how it can be done.

Download lvdragimage.zip (27kb)
Last update: 08/30/99

SystemImagelist: How to associate the system imagelist with the VB ListView.

To display icons in the VB ListView, typically a VB ImageList would be added to the project, filled with icons, and associated with the ListView's Icons and/or SmallIcons properties. But what if you wanted the ListView to use the icons from the system imagelist just as Explorer does? The solution seems obvious enough, send the ListView a LVM_SETIMAGELIST message, specifying the system imagelist's handle for either or both small or large icons. Well, that works, but there are a few more details to consider...

First, the VB ListView only knows how to talk to and use images from a VB ImageList. As a result, after assigning the system imagelist to the ListView with the LVM_SETIMAGELIST message, when it comes time for the ListView to display or update ListItem icons, the ListView will then send itself another LVM_SETIMAGELIST message, specifying the handle of the VB ImageList associated with its current view (either the Icons or SmallIcons ImageLists). If no VB ImageList is associated with the current view,  the ListView will then specify 0 for the message's lParam value. Either way, your system imagelist assignment is removed and the ListView doesn't use its icons. The solution? Assign the system imagelist to the ListView with the LVM_SETIMAGELIST message, subclass the ListView, and eat (don't call CallWindowProc) all subsequent LVM_SETIMAGELIST messages that the ListView sends itself.

Next, since the ListView is now using the system imagelist, it no longer needs a VB ImageList. In fact, as far as the VB ListView's internal code is concerned, it's not using an ImageList. But how do we assign the image indices to each icon? Each ListView icon's image index must now be explicitly set with the listview's LVM_SETITEM message. The real listview code that the VB ListView encapsulates will then automatically display the icon for each ListItem for the icon's specified index within the system imagelist.

But this method presents one problem little. Since there is no VB ImageList holding icon images for the ListView to talk to, each ListItem's Icon and SmallIcon properties are uninitialized, and cannot be used. In order to retrieve an ListItem's Icon and SmallIcon in a Picture object, a four part process must now be used. First the real listview icon index of the specified item must be obtained (real listview item indices correspond to zero-based *physical item positions* in the listview, as opposed to ListItem.Index that corresponds to the one-based position in the ListItems collection). Second, the specified item's assigned icon index in the system imagelist is obtained with the LVM_GETITEM listview message. Third, the item's icon handle is obtained from its system imagelist index with the ImageList_GetIcon API. And finally, the icon handle is converted into a Picture object with the OleCreatePictureIndirect API.

Though much more code intensive and complicated than using the VB ImageList, assigning a real imagelist to the ListView is not only many more times faster than the VB ImageList, but is also incredibly less resource intensive as well (the real imagelist use only one GDI handle for a single bitmap that includes all icon images within it, where anywhere from two to four GDI handles are used **for each ListImage in the VB ImageList**). Not much of consideration there...

The following code example demonstrates all which was discussed above by showing not only how to fill the ListView with all icons found in the system imagelist, but also how to fill the ListView with all icons of registered file types, including their corresponding file type descriptions.

Download systemimagelist.zip (11kb)
Last update: 08/30/99

LVCustomDraw: How to use custom draw in the ListView

Listview custom draw allows any font, text color, and background color to be specified for any or all ListItems. No long explanation about what custom draw is, and how it works, just a demo that shows the interesting effects it can produce (uses subclassing).

Download lvcustomdraw.zip (9kb)
Last update: 08/30/99

LVItemTree: How to create a hierarchical ListView (with buttons like a treeview)

This example shows how to set ListView item state images and indent values simulating a hierarchical tree with expandable and collapsible items, identical to that of the Outlook news reader's message list pane.

The working code is fairly simple: a "+" state icon is added to a collapsed parent ListItem with the LVM_SETITEM message; when the parent ListItem is expanded, the state icon is replaced with the "-" icon; as each child ListItem is Added under the expanded parent, its indent is increased by setting the LVITEM's iIndent member, and is also given a "+" state icon. When an expanded parent ListItem is collapsed, the "-" state icon is replaced with the "+" icon, and all ListItems with an indent value greater than the parent ListItem are removed.

11-14-99 update (v1.20)
- Now toggles the expanded state of parent items on a left mouse button double click
- Now scrolls selected items and child items of expanded parent items fully into view
- Modified keyboard support for expanding/collapsing parent items providing keyboard navigational functionality identical to that of the TreeView.
- Now provides functionality for the full range of TreeView Node relational properties: Parent, Child, FirstSibling, LastSibling, PrevSibling, NextSibling; and then some: Parents, Siblings, Children

10-24-99 update (v1.10)
- Added keyboard support for expanding/collapsing parent items
- Added dynamic column header resizing

09-17-99 initial release (v1.00)

Download lvitemtree2.zip (8kb)
Last update: 11/14/99

LVHeaderSortIcons: How to display custom sort icons in ListView column headers

Another fairly simple, yet useful example showing how to display the "\/" and "/\" icons in ListView column headers indicating the actively sorted column and its sort direction.

The demo contains a class that can be added to any project that uses a ListView in report view. The class creates its own real imagelist with the ImageList_Create API, and the two sort direction icons are added with the ImageList_AddIcon macro. When a ListView ColumnHeader is clicked, the client calls the class' SetHeaderIcons method, specifying the active ColumnHeader's Index, and its sort direction.The code in the method then removes any icon from the previously sorted ColumnHeader, and sets the new icon with the HDM_SETITEM header message. Note that this code only works on IE3 and greater installations.

Much of this demo's inspiration was drawn from Randy Birch's "How to Add Images to a ListView Header" code at http://www.mvps.org/vbnet/code/comctl/lvheaderimage.htm, which does things a bit differently...

Download lvheadersorticons.zip (5kb)
Last update: 10/03/99

VBDirectoryLV: How to create a hierarchical ListView that thinks its a TreeView (and displays directories)

This demo picks up where the LVItemTree demo above left off, and incorporates all of the functionality in the Directory TreeView demo. Though the result is code not having a whole lot of practical every day value, it is rather interesting study of ListView functionality none the less: a ListView that does folders just like VB DirListBox.

For all intents and purposes, the ListView in this demo looks and behaves exactly like the TreeView, and it is difficult to distinguish the differences, except for three key elements: no tree lines; folder icons become selected along with their item labels; and there are no item tips for obscured item labels. But other than that, the ListView is a TreeView.

Download vbdirectorylv.zip (19kb)
Last update: 11/14/99

LVItemTips: ListView item tips galore

As always, the demos available here tend to be a little more involved and bigger than most, but this one pushes the ragged edge of what may be viewed as practical and usable. It none the less does provide some rather necessary functionality: item tips anywhere any everywhere in the VB ListView, including report view subitems.

Attempting to describe what the demo does and why it does it could take thousands of words, it's just not a simple task. But the demo's concept is essentially this: a class module is used to create, position and control the visibility of a real tooltip common control, allowing it to appear over any ListItem or SubItem whose label text is not entirely visible (either obscured by the ListView's client window or truncated with an ellipsis), in all ListView views. Though the code is as always heavily commented, even if it can't be fully understood, it least what it does can be appreciated...

The class has been tested on and was found to work successfully with both the Comctl32.ocx and Mscomctl.ocx ListViews, the real common control listview, and on all current flavors of Windows that are running Comctl32.dll v4.70 (IE3) and greater.

Download lvitemtips.zip (15kb)
Last update: 11/14/99

Miscellaneous ListView demos that never got much attention

Below is a list of VB ListView demos that were developed in response to newsgroup inquiries. Though they weren't deemed worthy of having their own separate sections here on this page, they still may provide some usefulness to those interested...

setshellview1.zip (3kb) Shows how to change the view of any SHELLDLL_DefView listview, including the desktop and common dialog listviews. (last update 08/12/99)

lvselectionex.zip (3kb) Shows how to provide individual item click selection in the VB ListView so that other selected items do not become de-selected, identical to a multiselect ListBox. (last update 09/26/99)

lvsubitemedit.zip (5kb) Shows how to provide in-place editing of VB ListView report view subitems. Though not 100% clean in functionality, this demo does provide the essential core concept of positioning a VB TextBox directly over subitems. (last update 10/04/99)

lv16x16dragicon.zip (10kb) Though nowhere near the robustness of the LV Drag Image demo above, this demo does provide some rather useful code: it shows two distinct methods of creating a 32x32 drag image from a 16x16 drag image in code (ala CreateDragImage) that can be assigned directly to the ListView's DragIcon property. (last update 11/01/99)