Search code examples
c++mfclegacyclistctrl

How to handle CListCtrl group state change (collapsed\expanded) in MFC


I'm implementing grouping for CListCtrl in legacy MFC application (that part of code has comments from 2000).

There is custom class that inherited (indirect) from CListCtrl there is ~2k lines of code in the .cpp file of that class.

Actual data that represented by this ListCtrl is being pooled from some service and cached in data member of that custom derived list class.

Underlying CListCtrl itself contains items as empty lines, without text or any other data except lParam which is set to an id of actual data item and now it also has iGroupId

listItem.mask = LVIF_PARAM | LVIF_GROUPID;

All actual data painted out in list by some custom behavior, for icons it calls ImageList_DrawEx(), for text DrawText() instead of SetItemText(), in one column there is rectagle drawing progress bar and for one column it also places a button in an apropriate to row\column rectangle.

So when I added LVGS_COLLAPSIBLE to enable grouping on list it somehow worked well with all that custom rendering, except for buttons, which is not hides when items is being collapsed with it's group. Buttons stays where they were and do not moved and overlaps other items, because other items getting redraws and those buttons do not.

EDIT: those are real buttons stored in data member of derived list class, they are created with CButton::Create() in given coords on screen and then being moved around with CWnd::MoveWindow() when needed

I figured that I can simply hide\remove buttons when item draw function occurs on item in collapsed group, but that draw function being called on item only when item is visible and not collapsed.

So I need a way to handle change of group state, to call that item draw manually, but I don't collapse\expand groups myself.

All groups state is LVGS_COLLAPSIBLE or (0x08) so they can be collapsed\expanded in GUI by default and I can check if it's state is LVGS_COLLAPSED or (0x01)

// idOfChangedGroup = ???;

LVGROUP group = { 0 };
group.cbSize = sizeof(group);
group.mask = LVGF_STATE;
group.stateMask = LVGS_NORMAL | LVGS_COLLAPSIBLE | LVGS_COLLAPSED;
GetGroupInfo(idOfChangedGroup, &group);

if (group.state & LVGS_COLLAPSED){
    // hide button associated with item
}
else{
    // show button associated with item
}

The problem is - I can't figure out where to get id of group that been collapsed\expanded in GUI.

I'm not proficient in MFC, but I think it should be done somehow within OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) or OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult), as I understand those funcs is handling messages\events from framework\system, so I believe there is a way to handle group state change.

But I cant find any notification messages related to change of group state and clear guide\example of how to do it.

Please give me some advice or direction! That stuff is so OLD and I'm struggling finding any useful info...


Solution

  • Strangely enough, the Win32 List-View control documentation doesn't mention any notification about groups or group states.

    Searched the web and found that list-view controls do actually send such notifications, but they are undocumented unfortunately. Take a look at this article. It also contains some sample code demonstrating its use. Dates back to 2012, so you have to do some testing to check whether it still applies.

    It seems this feature is actually used by some libraries and developers; take a look into this file on GitHub. Also, the .NET ListView class has a OnGroupCollapsedStateChanged() event. And while the .NET controls may not be native, their Windows Forms implemntations are, ie the ListView class encapsulates a native or almost native Win32 List-View control. Very likely .NET uses this undocumented feature too. Couldn't find the actual .NET source, if someone can please post.