In a previous post, I used the column header to sort the ListView. Now I’ve abandoned sorting and I’m using column headers for something else.
I need a way to easily add the same data to all of the ListItems. My ListItems are products and each product has an associated Board Software Package (BSP). The vast majority of the time, the BSP will be the same for every product on the order. There are situations where this is not true, but mostly it is. I need to add “v14.50? to each BSP column of the ListView to indicate the version that each product gets.
My original approach was to make the ListView multiselect. I envisioned that the user would select all the items and even added a “Select All” button to facilitate that. The textboxes used to edit the data normally would show the information for the selected item. But if more than one item is selected, I would have to do something different. My thought was that I would loop through all of the selected items and only display the information if it was the same for each item. If two items are selected and they both have “v14.50? in the BSP column, then I would show “v14.50? in the BSP textbox. If, however, one item has “v14.50? and the other has “v14.30?, I would show “Multiple” in the textbox.
I began coding that piece and immediately I didn’t like it. The ListView’s ItemCheck event passes an Item variable – only one Item, not an array of all the checked Items. That means the event will fire once for every Item that is checked and the event would loop through each Item in the ListView. It seemed very cumbersome, so I started looking for a different approach.
I feel like I’m trading ease of use for ease of coding. At some point in my coding career, I established (in my own mind) a correlation between how easy something is to code and how easy it is to use. I suspect I did this because I love nice, clean code. And probably because I’m lazy.
My final approach was to use the ColumnClick event. First, I changed the Multiselect property to False. This made coding the textboxes much easier.
Set mobjListItem2 = Item
UnlockTextBoxes 2, Item.Selected
FillTextBoxes 2, Item, Item.Selected
Private Sub UnlockTextBoxes(ByVal lPage As Long, ByVal bSelected As Boolean)
Select Case lPage
Me.tbxBSP.Locked = Not bSelected
Me.tbxMain.Locked = Not bSelected
Me.tbxPBI1.Locked = Not bSelected
Me.tbxPBI2.Locked = Not bSelected
Me.tbxKeyCode.Locked = Not bSelected
Private Sub FillTextBoxes(ByVal lPage As Long, Item As MSComctlLib.ListItem, _
ByVal bSelected As Boolean)
Select Case lPage
Me.tbxItem2.Text = IIf(bSelected, Item.Text, “”)
Me.tbxDesc2.Text = IIf(bSelected, Item.SubItems(1), “”)
Me.tbxBSP.Text = IIf(bSelected, Item.SubItems(2), “”)
Me.tbxMain.Text = IIf(bSelected, Item.SubItems(3), “”)
Me.tbxPBI1.Text = IIf(bSelected, Item.SubItems(4), “”)
Me.tbxPBI2.Text = IIf(bSelected, Item.SubItems(5), “”)
Me.tbxKeyCode.Text = IIf(bSelected, Item.SubItems(6), “”)
Don’t mind all the 2’s and Select Cases. I have ListViews on each page of a Multipage control and I want to use the same procedures to fill the textboxes. This approach allows me to hold the currently selected Item in a module-level variable which makes it much easier to write back to the ListView when something in a textbox changes.
mobjListItem2.SubItems(2) = Me.tbxBSP.Text
To handle multiple row updates, I use the ColumnClick event. The first two columns aren’t allowed to be changed, so I start my Select Case with column 3. The Select Case sets a Textbox variable and the loop writes that textbox’s Text property to each row.
Dim i As Long
Dim tbx As MSForms.TextBox
Dim lSubItem As Long
lSubItem = ColumnHeader.Position – 1
Select Case ColumnHeader.Position
Set tbx = Me.tbxBSP
Set tbx = Me.tbxMain
Set tbx = Me.tbxPBI1
Set tbx = Me.tbxPBI2
Set tbx = Me.tbxKeyCode
If Not tbx Is Nothing Then
For i = 1 To .ListItems.Count
.ListItems(i).SubItems(lSubItem) = tbx.Text
I’ve definitely sacrificed some usability here. The first thing I sacrificed is intuitiveness. While most people are familiar with multiselect, particularly when there are checkboxes next to each item, nobody without proper training is going to know to click on the column header. Not that the multiselect route was perfect. I had a hard time envisioning how that was going to work. For instance, I could see someone accidentally filling “Multiple” into every Item, which would be bad.
The other sacrifice my users unknowingly made is the ability to update multiple rows while leaving others unchanged. The ColumnClick approach updates all items, which is less usable than being able to update, say, half of them. Updating all items will be the proper course in about 90% of the cases, so I don’t feel too bad about it.
Then there’s the abandonment of the sort feature, which I won’t miss at all.
I made a few other changes to the ListView in the process. I set the LabelEdit property to lvwManual. I have no idea what lvwManual is supposed to represent, but it means the user can’t edit the Item inside the ListView. I was not able to set FullRowSelect to True and have it persist, so I had to set it in code in the Initialize procedure.