
So I've mentioned this and posted snippets in a few threads, but thought it would be good to do a formal sample project on this, especially since I've never seen one done before.
By default, the ListView and TreeView controls, whether it's from the OCX or manually created, only has the basic checked or unchecked state. But what if you want to add the Partial check state? Or even more? Or customize the regular checked and unchecked look? Most of the time people jump to owner draw, but there's a much simpler way: checkboxes are simply an imagelist, so all you have to do is create your own and assign it just like you do for the regular icons, no owner drawing required. The ListView/TreeView even manages the number of checkboxes for you; no special code is required to cycle through all the checkboxes then loop back to the beginning. There's 8 different checkboxes in the sample project, I'm not sure what the limit is but you almost certainly won't hit it.
The only thing that makes this even a little complex is that you have to drop down to the API level to set the imagelist, and subclass it just to prevent VB from accidentally removing the imagelist. The good news though is that it's entirely possible to do it with the regular Common Controls 5.0 ListView/TreeView control, which is what the sample project uses.
The new checkboxes are stored in a resource file and accessed from there, but I've also included the .ico's as normal files in the zip.
How it works
First we create a new API ImageList with our new checkboxes:
Code:
Dim hIco As Long
himlCheck = ImageList_Create(32, 32, ILC_COLOR32 Or ILC_ORIGINALSIZE, 1, 1)
ImageList_SetIconSize himlCheck, 16, 16
hIco = ResIconTohIcon("CHK_STD_UNCHKD", 16, 16)
Call ImageList_AddIcon(himlCheck, hIco)
Call DestroyIcon(hIco)
'rinse and repeat for all other checkboxes. Note that if you're doing this with a TreeView,
'you need a blank icon (not unchecked, entirely blank) as the first image, but with the ListView
'you just start with the first box in the series- usually unchecked.
ListView_SetImageList hLVS, himlCheck, LVSIL_STATE
That's all you have to do to get started- all items will default to the first checkbox in the list, then cycle through in order with each click, then after the last one returns to the beginning.
If you want to set the check state through code, you need to use API since True/False isn't good enough,
Code:
Dim li As ListItem
Dim lvi As LVITEM
lvi.iItem = li.Index - 1 'get your li from ListView.Items.Add() and similar
lvi.Mask = LVIF_STATE
lvi.StateMask = LVIS_STATEIMAGEMASK
lvi.State = IndexToStateImageMask(k) 'where k is the 1-based index of the checkbox you want
ListView_SetItem ListView1.hWnd, lvi
CheckIndex = StateImageMaskToIndex(ListView_GetItemState(hLVS, iItem, LVIS_STATEIMAGEMASK)) 'where iItem is zero-based
The procedure for the TreeView is virtually identical, with the important step of adding the blank image mentioned earlier, and needing to get the hItem since the APIs don't use the index (TVITEM.hItem = pvGetHItem(Comctllib.Node))
That covers the basic concept, all the other code is just standard setup.
Requirements
-Windows Vista or higher (although everything is listed as available in comctl32 6.0, it seems XP isn't going to work :( )
-Common Controls 6.0 Manifest - The sample project has the cc6.0 manifest embedded in its resource file so it will work when compiled, but to work in the IDE your VB6.exe must also be set up to use the 6.0 controls. See LaVolpe's excellent manifest creator project to generate the manifest and startup code for your own projects.