Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

LVM_ENABLEGROUPVIEW on a win32 listview (common controls 6) returning 0

Status
Not open for further replies.

Nigel Gomm

Programmer
Jan 10, 2001
423
0
0
CA
I'm trying to get groups working in a listview.

Code:
#define LVM_FIRST               0x1000
#DEFINE LVM_ENABLEGROUPVIEW     LVM_FIRST + 157
#define LVM_ISGROUPVIEWENABLED  LVM_FIRST + 175

DECLARE LONG SendMessage IN win32api AS sendMyMessage;
    LONG HWND, INTEGER Msg, LONG wParam, INTEGER LPARAM

sendMyMessage(THIS.HWND, LVM_ENABLEGROUPVIEW,1,0)
This is always returning 0 when it should be returning 1 (and groups are not showing). Sending LVM_ISGROUPVIEWENABLED returns 0 as well.

This is v6.0 (SP6) of the common controls (as confirmed by .aboutbox).

Has anyone got this working?

test code adds an item then adds a group and then assigns the item to that group. But LVM_ENABLEGROUPVIEW always returns 0 (not even -1 to indicate failure) whether issued before the items are added or after. I can see that my LVM_INSERTGROUP is also returning 0 which could be interpreted as an index.... except that a subsequent LVM_GETGROUPCOUNT also returns 0.

My application manifest is the default (and checked within visual studio) enabling common controls 6. Also reinstalled mscommctl just to be sure (but as i said .aboutbox confirms it is v6 (SP6)..


tia

n
 
Do you have some more code to reproduce the problem? How you add data and groups to the listview? I assume by ListItems.Add()?
And what HWND is "THIS.Hwnd"? Is THIS the olecontrol itself?



Chriss
 
Chris,
correct re "this.hwnd" and .listitems.add()

It may be that you can't add a group till at least one item is added but sample code around the net (nonVP) suggests that the LVM_ENABLEGROUPVIEW should work even before any items or groups or added.


Code:
THIS.mylistview1.Icons = THIS.olECONTROL1 && icons
THIS.mylistview1.smallIcons = THIS.olECONTROL1
THIS.mylistview1.ListItems.CLEAR()
THIS.mylistview1.arrange = 2  && lvwAutoTop
THIS.mylistview1.VIEW = 0

loItem = THIS.mylistview1.ListItems.ADD(1,"xx","Test1",4,4)
loItem.ICON = 2
loItem = THIS.mylistview1.ListItems.ADD(2,"xy","Test2",4,4)
loItem.ICON = 3
		
THIS.mylistview1.addgroup(1,"Media")
THIS.mylistview1.additemtogroup(1,1)
THIS.mylistview1.additemtogroup(2,1)
*
*
*
THIS.mylistview1.showgroups = .T.
*
*
*
*
#define LVM_FIRST   			0x1000
#DEFINE LVM_ENABLEGROUPVIEW   	(LVM_FIRST + 157)
#define LVM_ISGROUPVIEWENABLED  (LVM_FIRST+175)
#define LVM_GETGROUPCOUNT 		(LVM_FIRST + 152)
#define LVM_UPDATE 				(LVM_FIRST+42)
#DEFINE LVM_INSERTGROUP  		(LVM_FIRST + 145)
#DEFINE LVM_MOVEITEMTOGROUP  	(LVM_FIRST + 154)
#define LVM_SETITEMW	  		(LVM_FIRST + 76)
#define LVM_SETITEM  			(LVM_FIRST + 6)
#define LVM_GETTEXTCOLOR 		(LVM_FIRST+35)
#define LVM_GETTEXTBKCOLOR 		(LVM_FIRST+37)
#define LVIF_GROUPID 			0x00000010
#define LVGF_HEADER 			0x00000001
#DEFINE LVGF_ALIGN                      0x00000008
#DEFINE LVGA_HEADER_LEFT   		0x00000001
#DEFINE LVGA_HEADER_CENTER 		0x00000002
#DEFINE LVGA_HEADER_RIGHT  		0x00000004

DECLARE LONG SendMessage IN win32api AS sendMyMessage;
	LONG HWND, INTEGER Msg, LONG wParam, INTEGER LPARAM

DECLARE LONG SendMessage IN win32api AS sendLVMessage;
	LONG HWND, INTEGER Msg, LONG wParam, STRING @LPARAM
*
*
*
*
**************************************************
*-- Class:        mylistview (c:\users\nigel\documents\visual foxpro projects\rentman5.2\classes\baseclasses.vcx)
*-- ParentClass:  olecontrol
*-- BaseClass:    olecontrol
*-- Time Stamp:   08/30/21 11:16:09 AM
*-- OLEObject = C:\Windows\SysWOW64\MSCOMCTL.OCX
*
#INCLUDE "c:\users\nigel\documents\visual foxpro projects\rentman5.2\rentman.h"
*
DEFINE CLASS mylistview AS olecontrol


	Height = 100
	Width = 100
	Anchor = 15
	showgroups = .F.
	Name = "mylistview"


	PROCEDURE showgroups_assign
		LPARAMETERS vNewVal
		THIS.showgroups = m.vNewVal

		IF sendMyMessage(THIS.HWND, LVM_ENABLEGROUPVIEW,IIF(vNewVal,1,0),0) < 0
			=diagnostic("LVM_ENABLEGROUPVIEW failed")
		ENDIF
	ENDPROC


	PROCEDURE addgroup
		LPARAMETERS tvGroup,tcHeader
		LOCAL lcDef,lcStruct,loStruct,lnResult


		TEXT TO m.lcDef noshow
				typedef struct tagLVGROUP {
				  UINT   cbSize;
				  UINT   mask;
				  LPWSTR pszHeader;
				  int    cchHeader;
				  LPWSTR pszFooter;
				  int    cchFooter;
				  int    iGroupId;
				  UINT   stateMask;
				  UINT   state;
				  UINT   uAlign;
				  LPWSTR pszSubtitle;
				  UINT   cchSubtitle;
				  LPWSTR pszTask;
				  UINT   cchTask;
				  LPWSTR pszDescriptionTop;
				  UINT   cchDescriptionTop;
				  LPWSTR pszDescriptionBottom;
				  UINT   cchDescriptionBottom;
				  int    iTitleImage;
				  int    iExtendedImage;
				  int    iFirstItem;
				  UINT   cItems;
				  LPWSTR pszSubsetTitle;
				  UINT   cchSubsetTitle;
				} LVGROUP, *PLVGROUP;
		ENDTEXT

		loStruct=CREATEOBJECT("rAPIdStructure")
		loStruct.LoadCDef(m.lcDef)
		loStruct.lAutoMemory = .T.
		WITH loStruct
			.mask = LVIF_GROUPID+LVGF_HEADER
			.iGroupId = tvGroup
			.pszHeader = tcHeader
			.cchheader = LEN(.pszHeader)
			.pszfooter = ""
			.cchfooter = LEN(.pszfooter)
			.cbsize = .calculatebytes()
			.ualign = LVGA_HEADER_LEFT
		ENDWITH
		m.lcStruct =  loStruct.makestruct()
		m.lnResult= sendLVMessage(THIS.HWND, LVM_INSERTGROUP , -1, @m.lcStruct)
		IF m.lnResult < 0
			=diagnostic("LVIF_GROUPID failed")
		ENDIF
		RETURN m.lnResult &&the group's index


	ENDPROC


	PROCEDURE additemtogroup
		LPARAMETERS tnItem,tvGroupID
		LOCAL loLVItem,lcDef,lcStruct

		TEXT TO m.lcdef noshow
				typedef struct tagLVITEMA {
				  UINT   mask;
				  int    iItem;
				  int    iSubItem;
				  UINT   state;
				  UINT   stateMask;
				  LPSTR  pszText;
				  int    cchTextMax;
				  int    iImage;
				  LPARAM lParam;
				  int    iIndent;
				  int    iGroupId;
				  UINT   cColumns;
				  PUINT  puColumns;
				  int    *piColFmt;
				  int    iGroup;
				} LVITEMA, *LPLVITEMA;
		ENDTEXT

		loLVItem=CREATEOBJECT("rAPIdStructure")
		loLVItem.LoadCDef(m.lcDef)


		WITH loLVItem
			.mask = LVIF_GROUPID
			.iItem = tnItem - 1 &&Correct to base-0 index for this usage.
			.iGroupId = tvGroupID
		ENDWITH
		m.lcStruct = loLVItem.makestruct()
		IF sendLVMessage(THIS.HWND, LVM_SETITEM , 0,@m.lcStruct ) = 0
			=diagnostic("LVM_SETITEM failed")
		ENDIF
	ENDPROC


ENDDEFINE
*
*-- EndDefine: mylistview
**************************************************

i also tried using struct.vcx but no difference,

n


p.s. that rapidstructure class is here Link
 
I can't run this, can you provide an example which works (except for the bug, of course)?

I think you have a wrong result handling.

According to sending the LVM_INSERTGROUP message, the return value for an error is -1, or the index of the new group (including 0).
According to sending the LVM_SETITEM message, the return value for an error is 0.

In C false is 0, any other value is true, usually true is defined as -1.

And you both check for <0, what do you do in the diagnostics prg? Do you use GetLastError to see what goes wrong?



Chriss
 
Chriss,

sendmymessage with LVM_ENABLEGROUPVIEW never returns -1 always returns 0 but a subsequent call using LVM_GETGROUPCOUNT or LVM_ISGROUPVIEWENABLED also returns 0 (and groups aren't showing).

diagnostic() just posts the message and ASTACKINFO to a table.

i'll post a runnable something this afternoon.

(the weekend wasn't my own so i just pasted more or less direct from the project)

n
 
extract zip file to temp folder, set default to same folder and then do form testlistview.scx


n

n.b. you'll see in this sample Delphi code that the LVM_ENABLEGROUPVIEW is expected to work even before items and groups are added. Of course i'm not able to test this to see if it actually works... but have some similar VB6 code where same is true.


edit: sorry, there was a typo; should be
#define LVIF_GROUPID 0x100
but doesn't affect the enablegroupview.
 
 https://files.engineering.com/getfile.aspx?folder=e742df0a-47aa-436d-892a-fc97924a00ce&file=testlistview.zip
Thanks Nigel, I started working on it - strange.

If the struct works and the result 0 of LVM_ENABLEGROUPVIEW is really what is documented as:
MS Docs said:
The ability to display list-view items as a group is already enabled or disabled.
Aside from that meaning nothing (yes, we know, this boolean status can only be true or false).

So, the 0 result coming from VM_ISGROUPVIEWENABLED would mean the LVM_ENABLEGROUPVIEW message sent was interpreted as wanting to disable group view.
Maybe rAPIdstrucutre is failing for this struct for any reason.

I guess you also found how the manifest of an EXE has to look when using ComCtl32.dll Version 6 (and that you should call InitCommonControls.

Since, in general, common controls version 6 work in VFP9, I guess both VFP9.exe and the VFP runtimes are initializing the common controls, so you don't have to do that. Indeed the manifest embedded in VFP executable is having the common controls section, also the manifest embedded in VFP9 is having the needed section. It may not be a red herring for one reason, though: The MS Docs on InitCommonControls say today you better call InitCommonControlsEx (bad name choice btw).

I am still playing around with that and with defining the struct another way. I remember for some API calls you can't get away with VFPs mechanism of passing a STRING@ as memory address of the string, and I see you also define SendMessage to pass in an address as INTEGER. And that's compatible to defining the parameter as String@, but needs memory management. rAPIdstructure does that, but I guess something may go wrong there.

When a struct is necessary for the memory has to stay allocated to be kept at the same address and deallocation has to wait for some finishing step.

Long story short, I'll try managing the struct for the SendMessage without rAPIdstructure. You might get there before me.

Chriss
 
Chriss,

The LVM_ENABLEGROUPVIEW should work (i.e. return 1) BEFORE we need to worry about adding groups and therefore structures so i wasn't too worried about rstruct creating the correct structure yet.

Do you have VB6 installed? I'll attach something i downloaded from somewhere (frm/frx) that is supposed to implement groups. The code might prove useful anyway.

appreciate the interest.

n
 
 https://files.engineering.com/getfile.aspx?folder=2e61f4e7-1020-436b-9885-181041ab44e5&file=Module1.zip
Chriss,

the documentation led me to believe that i didn't need to explicitly call initCommonControlsEx (it's registered and there in my manifest) but the way it seems to ignore the LVM_ENABLEGROUPVIEW makes me think that is not the case.

Early attempts with
Code:
Declare long InitCommonControlsEx in comctl32.dll
initcommoncontrolsEx()

don't change anything.

n
 
InitCommonControlsEx is having a struct parameter you need to set.
See
I still haven't got this to work, and another news2news sample now on VFPXs github is also not using groups, but perhaps a better way to create the struct:

I'll continue on the weekend, maybe you can figure it out earlier, with the help of these samples.

Chriss
 
I once had Pascal (Borland Turbo Pascal) but not Delphi. Today you can of course get any language for free or cheap, but just looking at the Delphi code it doesn't do anything more special then sending the same messages your code does, unless the struct creation is somehow wrong or the constants or some prerequisite setting.

The Delphi code first enables group view and then creates groups and adds items to them. I think its targeting XP and so it really would be interesting whether this Delphi code would work in Windows Vista or later and not only in Win95 or XP.

Another interesting thread about why group view may fail:

Especially interesting, as its counterintuitive:
do not include the ICC_LISTVIEW_CLASSES flag as in some cases it might prevent the listview control from displaying groups.


It mentions grouping in virtual mode and that leads to the question is VFP using common controls in virtual mode and what is that and when grouping in that mode is special is it or wh is it not available in virtual mode without knowing some secret?

I'll now start from the VFPX sample listing all system icons/symbols and try to add groups to that sample. I take how VFPX win32api creates the struct(s).




Chriss
 
Chriss,

tried without the ICC_LISTVIEW_CLASSES flag and no change.

I had also tested to see if the listview is in virtual mode ... and its not.

n
 
Okay, I reached far and installed Embarcadero Delphi Community Edition.

The Delphi example works with it.

I adjusted both Delphi and your Code to add the items:

Code:
ListItem := ListView1.Items.Add;
    ListItem.Caption := 'Test1';
...and Test2, Test3

Code:
loItem = THIS.listview1.ListItems.Add()
loItem.Text = 'Test1'
...and Test2, Test3

Up to there no struct was used. But you already see a slight difference in that the Ole ListItem object returned from Listview.Items.Add() has a Caption property while in VFP it has a Text property.
Not sure where that comes from. All I know is Delphi has its own set of types and classes all related to Win32api, it also compiles into a x86 (32bit) EXE.

Next in Delphi is sending the LVM_ENABLEGROUPVIEW message, which returns 1 there, but still 0 in the VFP scx.

I don't know what's going wrong up to that point already. I learned the order of events has to be
1. add items
2. enable groupview
3. add groups
4. assign groupids to the items from step1 by LVM_SETITEM

But the VFP form also adds items first and already fails at step 2, as if something is missing or wrong.
The Viewstylee in Delphi also is Icons by default, so it's not the ListView.View property, that's hindering to enable the groupview.

Finally here's the manifest from the Delphi exe:
Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
  <asmv3:application>
    <asmv3:windowsSettings>
      <dpiAware xmlns="[URL unfurl="true"]http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>[/URL]
      <dpiAwareness xmlns="[URL unfurl="true"]http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>[/URL]
    </asmv3:windowsSettings>
  </asmv3:application>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        publicKeyToken="6595b64144ccf1df"
        language="*"
        processorArchitecture="*"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"
        />
      </requestedPrivileges>
    </security>
  </trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 
	<application> 
		<!--The ID below indicates app support for Windows Vista -->
		<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> 
		<!--The ID below indicates app support for Windows 7 -->
		<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
		<!--The ID below indicates app support for Windows 8 -->
		<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
		<!--The ID below indicates app support for Windows 8.1 -->
		<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
		<!--The ID below indicates app support for Windows 10 -->
		<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>			
	</application> 
</compatibility>
</assembly>

A VFP manifest has
Code:
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            language="*"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
        />
    </dependentAssembly>

But I don't think it's a matter of setting processorArchitecture="*", which is the only difference directly related to Common Controls in the manifest. I have set the Delphi Project to compile to Windows 32.

I guess Delphi is better at already having all the Windows win32 types as Delphi types implementation. The secret of it working might be in there, but so far I have no idea how to dig out what differs in a Delphi TListView. I ran dependency walker on the EXE Delphi creates, but that churns on it for half an hour now and still doesn't show anything.



Chriss
 
Dependency Walker has finally finished. It drilled down references of Kernel32.DLL that led to circular references through some DLL going back to Kernel32 which made it go to extremes.

Well, in the end it shows the expected dependency to COMCTL32.DLL


Chriss
 
tried an .exe with that delphi manifest (less the dpiaware stuff) and no change.

n
 
I went back to It may be something you have no direct control about, the point about LVS_ALIGNTOP. That would involve the GetWindowLong and SetWindowLong API calls.

When you look up CommCtrl.h (for example given in LVS_ALIGNTOP is defined as 0. I'm puzzled how you could set a flag on or off, when its value is 0 and you combine it with other LVS_ Styles. Same is valid for LVS_ICON. It may boild down to NOT setting LVS_ALIGNLEFT (0x0800) besides some other flags, but I have no definitive source about what to do to ensure the ListView windos style is LVS_ALIGNTOP.

In the ListViewVtrl Properteis you only have the Arrange setting, which can be set to lvwNone, lvwAutoLeft, or lvwAutoTop. I think the Auto settings correspond to LVS_AUTOARRANGE. There you at least have restricted options in comparison with combining the available Window style flags.

I tried a few things and nothing worked. Sorry, at this point I'm out of ideas. Maybe sometime later. I wish you good look investigating from here. Maybe you look into using some other control.



Chriss
 
Chriss,

Appreciate the (considerable) effort you made. I may look at other controls but a grouped listview would have been perfect for the particular purpose i have in mind..... and really there's no reason it shouldn't work.

I'd pretty much tried everything i could think of before posting here.... you have at least confirmed its not just me and my PC with some sort of corruption.

n
 
Yes, and it's not Win10.

VFP has a special way of treating Windows, at least ActiveX controls are still having their own HWND, but the messaging with SendMessage seems not to work, though I know it does work in general, as many solution samples also show.

It's a bit disappointing, I'd now perhaps try to wrap this control and create an OCX VFP can use easier. In Delphi you simply have a proeprty GroupView that can be false or true.
Instead of sending the LVM_ENABLEGROUPVIEW message you can do ListView1.GroupView := True;

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top