Home » Blog [programming]
date 5.Feb.12

■ Undocumented collapsible listview control behavior

The system listview control is the most powerful GUI element available to windows programmers, and seldom you find any decent program without one. Its rich feature set was further enhanced with the advent of windows vista, with a host of improvements especially regarding the grouping feature available since windows XP. Vista/7 groups support headers, footers, subtitles, a task link and finally can be collapsed and restored.

The collapse/expand feature is especially useful in complex views. It allows the user to hide some of the less relevant items (see the pic) using the little arrow controls to the right of a group header. Unfortunately MS were again economical with the documentation (a sad trend nowadays). Creating a collapsible group is easy: just pass the LVGS_COLLAPSIBLE state flag in the defining LVGROUP structure. For a practical example see this codeproject article. However the documentation (or the lack of it) revealed a half-hearted implementation of collapsible grouping: how do you know when the user clicked the arrow to expand or collapse the group? How do you know when the group header is selected? A google search in codeproject, stackoverflow and similar sites indicates that nobody knew how to do it.

I was sure that there must be some undocumented listview notification message for group state change, so I went out looking for it — and sure enough it was there!
collapsible listview groups

CSI agents use DNA fingerprinting in their investigations, windows programmers have Spy++, a windows snooping tool included in all versions of MS Developer Studio. All I had to do was to hook it up with a listview control bearing LVGS_COLLAPSIBLE state and observe the flow of WM_NOTIFY messages while playing with the group features. Somewhere among the usual NM_CUSTOMDRAW messages was a WM_NOTIFY with code FFFFFF44 (LVN_FIRST-88) that Spy++ didn't know about. I added a notify handler for this code and bingo, that was the missing undocumented message, associated with all changes in group states. All too easy.

As you expect the NMHDR structure that contains the additional information about the grouping change notification was also undocumented. But that mystery didn't last too long either; a few debugger breakpoints later revealed this data structure:

// undocumented group state change notification code
#define LVN_GROUPINFO (LVN_FIRST-88)

// accompanying struct according to my investigations
typedef struct tagNMLVGROUP {
    NMHDR hdr;
    int iGroupId;   // which group is changing
    UINT uNewState; // LVGS_xxx flags
    UINT uOldState;
} NMLVGROUP,*LPNMLVGROUP;

I think there are more data members in this structure but all we care about is which group is changing (iGroupId) and how is it changing — which is revealed by comparing the old and new state flags. For example to see if a group is getting collapsed we check:

//LPNMHDR pnmh;
if(LVN_GROUPINFO == pnmh->code) {
	LPNMLVGROUP pLV = (LPNMLVGROUP)pnmh;
	if( (pLV->uNewState & LVGS_COLLAPSED) && !(pLV->uOldState & LVGS_COLLAPSED) )
		// getting collapsed
}

Similarly we can learn about all other group states e.g. when a group header is getting LVGS_SELECTED.

So there you have it, not rocket science but an effective mechanism to tap into the undocumented listview group notification messages. As with all undocumented features there is the risk that MS will change this behaviour in future windows versions (hello windows 8!) without offering an apology, but in this unlikely eventuality we will be back with another hacking article.

Post a comment on this topic »

Share |

©2002-2012 ZABKAT, all rights reserved | Privacy policy | Sitemap